stas 2002/12/09 08:29:58
Modified: src/docs/2.0/devel Changes.pod config.cfg
Added: src/docs/2.0/devel/core apache_integration.pod
coding_style.pod explained.pod
src/docs/2.0/devel/debug c.pod perl.pod
src/docs/2.0/devel/debug/code .debug-modperl-init
.debug-modperl-register .debug-modperl-xs
src/docs/2.0/devel/performance size_matters.pod
Removed: src/docs/2.0/devel/core_explained core_explained.pod
src/docs/2.0/devel/debug_c debug_c.pod
src/docs/2.0/devel/debug_c/code .debug-modperl-init
.debug-modperl-register .debug-modperl-xs
src/docs/2.0/devel/debug_perl debug_perl.pod
src/docs/2.0/devel/modperl_style modperl_style.pod
src/docs/2.0/devel/perf_sizeof perf_sizeof.pod
Log:
as the number of documents here grows, we need a better grouping. so this
commit just changes some dir/file names, no content change.
Revision Changes Path
1.3 +5 -0 modperl-docs/src/docs/2.0/devel/Changes.pod
Index: Changes.pod
===================================================================
RCS file: /home/cvs/modperl-docs/src/docs/2.0/devel/Changes.pod,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- Changes.pod 31 Jul 2002 14:16:48 -0000 1.2
+++ Changes.pod 9 Dec 2002 16:29:57 -0000 1.3
@@ -9,6 +9,11 @@
The most recent changes are listed first.
+=head1 ???
+
+* Reshuffled the dir/file structure to make more sense, as new docs
+ are getting added.
+
=head1 Wed Mar 20 21:20:20 SGT 2002
* docs::general::testing::testing:
1.12 +11 -6 modperl-docs/src/docs/2.0/devel/config.cfg
Index: config.cfg
===================================================================
RCS file: /home/cvs/modperl-docs/src/docs/2.0/devel/config.cfg,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- config.cfg 13 Aug 2002 11:46:21 -0000 1.11
+++ config.cfg 9 Dec 2002 16:29:57 -0000 1.12
@@ -10,16 +10,21 @@
group => 'mod_perl 2.0 Core Development',
chapters => [qw(
- core_explained/core_explained.pod
- modperl_style/modperl_style.pod
- perf_sizeof/perf_sizeof.pod
+ core/explained.pod
+ core/coding_style.pod
+ performance/size_matters.pod
)],
group => '3rd party modules Development with mod_perl 2.0',
chapters => [qw(
porting/porting.pod
- debug_perl/debug_perl.pod
- debug_c/debug_c.pod
+ )],
+
+ group => 'Debugging',
+ chapters => [qw(
+ porting/porting.pod
+ debug/perl.pod
+ debug/c.pod
)],
group => 'Help',
@@ -30,7 +35,7 @@
changes => 'Changes.pod',
copy_glob => [qw(
- debug_c/code
+ debug/code
)],
1.1
modperl-docs/src/docs/2.0/devel/core/apache_integration.pod
Index: apache_integration.pod
===================================================================
=head1 NAME
mod_perl internals: Apache 2.0 integration
=head1 Description
This document should help to understand the initialization, request
processing and shutdown process of the mod_perl module. This knowledge
is essential for a less-painful debugging experience. It should also
help to know where a new code should be added when a new feature is
added.
Make sure to read also: L<Debugging mod_perl C
Internals|docs::2.0::devel::debug::c>.
=head1 Startup
Apache starts itself and immediately restart itself. The following
sections discuss what happens to mod_perl during this period.
=head2 The Link Between mod_perl and httpd
I<mod_perl.c> defines a special structure:
module AP_MODULE_DECLARE_DATA perl_module = {
STANDARD20_MODULE_STUFF,
modperl_config_dir_create, /* dir config creater */
modperl_config_dir_merge, /* dir merger --- default is to override */
modperl_config_srv_create, /* server config */
modperl_config_srv_merge, /* merge server config */
modperl_cmds, /* table of config file commands */
modperl_register_hooks, /* register hooks */
};
Apache uses this structure to hook mod_perl in, and it specifies six
custom callbacks which Apache will call at various stages that will be
explained later.
C<STANDARD20_MODULE_STUFF> is a standard macro defined in
I<httpd-2.0/include/http_config.h>. Currently its main use if for
attaching Apache version magic numbers, so the previously compiled
module won't be attempted to be used with newer Apache versions, whose
API may have changed.
C<modperl_cmds> is a struct, that defines the mod_perl configuration
directives and the callbacks to be invoked for each of these.
=head1 Configuration Tree Building
At the C<ap_read_config> stage the configuration file is parsed and
stored in a parsed configuration tree is created. Some sections are
stored unmodified in the parsed configuration tree to be processed
after the C<pre_config> hooks were run. Other sections are processed
right away (e.g., the C<Include> directive includes extra
configuration and has to include it as soon as it was seen) and they
may or may not add a subtree to the configuration tree.
C<ap_build_config> feeds the configuration file lines from to
C<ap_build_config_sub>, which tokenizes the input, and uses the first
token as a potential directive (command). It then calls
C<ap_find_command_in_modules()> to find a module that has registered
that command (remember mod_perl has registered the directives in the
C<modperl_cmds> C<command_rec> array, which was passed to
C<ap_add_module> inside the C<perl_module> struct?). If that command
is found and it has the C<EXEC_ON_READ> flag set in its
I<req_override> field, the callback for that command is
invoked. Depending on the command, it may perform some action and
return (e.g., C<User foo>), or it may continue reading from the
configuration file and recursively execute other nested commands till
it's done (e.g., C<E<lt>Location ...E<gt>>). If the command is found
but the C<EXEC_ON_READ> flag is not set or the command is not found,
the current node gets added to the configuration tree and will be
processed during the C<ap_process_config_tree()> stage, after the
C<pre_config> stage will be over.
If the command needs to be executed at this stage as it was just
explained, C<execute_now()> invokes the corresponding callback with
C<invoke_cmd>.
Since C<LoadModule> directive has the C<EXEC_ON_READ> flag set, that
directive is executed as soon as it's seen and the modules its
supposed to load get loaded right away.
For mod_perl loaded as a DSO object, this is when mod_perl starts its
game.
=head2 Enabling the mod_perl Module and Installing its Callbacks
mod_perl can be loaded as a DSO object at startup time, or be
prelinked at compile time.
For statically linked mod_perl, Apache enables mod_perl by calling
C<ap_add_module()>, which happens during the
C<ap_setup_prelinked_modules()> stage. The latter is happening before
the configuration file is parsed.
When mod_perl is loaded as DSO:
<IfModule !mod_perl.c>
LoadModule perl_module "modules/mod_perl.so"
</IfModule>
mod_dso's C<load_module> first loads the shared mod_perl object, and
then immediately calls C<ap_add_loaded_module()> which calls
C<ap_add_module()> to enable mod_perl.
C<ap_add_module()> adds the C<perl_module> structure to the top of
chained module list and calls C<ap_register_hooks()> which calls the
C<modperl_register_hooks()> callback. This is the very first mod_perl
hook that's called by Apache.
C<modperl_register_hooks()> registers all the hooks that it wants to
be called by Apache when the appropriate time comes. That includes
configuration hooks, filter, connection and http protocol hooks. From
now on most of the relationship between httpd and mod_perl is done via
these hooks. Remember that in addition to these hooks, there are four
hooks that were registered with C<ap_add_module()>, and there are:
C<modperl_config_srv_create>, C<modperl_config_srv_merge>,
C<modperl_config_dir_create> and C<modperl_config_dir_merge>.
Finally after the hooks were registered,
C<ap_single_module_configure()> (called from mod_dso's C<load_module>
in case of DSO) runs the configuration process for the module. First
it calls the C<modperl_config_srv_create> callback for the main
server, followed by the C<modperl_config_dir_create> callback to
create a directory structure for the main server. Notice that it
passes C<NULL> for the directory path, since we at the very top level.
If you need to do something as early as possible at mod_perl's
startup, the C<modperl_register_hooks()> is the right place to do
that. For example we add a C<MODPERL2> define to the
C<ap_server_config_defines> here:
*(char **)apr_array_push(ap_server_config_defines) =
apr_pstrdup(p, "MODPERL2");
so the following code will work under mod_perl 2.0 enabled Apache
without explicitly passing C<-DMODPERL2> at the server startup:
<IfDefine MODPERL2>
# 2.0 configuration
PerlSwitches -wT
</IfDefine>
This section, of course, will see the define only if inserted after
the C<LoadModule perl_module ...>, because that's when
C<modperl_register_hooks> is called.
One inconvenience with using that hook, is that the server object is
not among its arguments, so if you need to access that object, the
next earliest function is C<modperl_config_srv_create()>. However
remember that it'll be called once for the main server and one more
time for each virtual host, that has something to do with mod_perl. So
if you need to invoke it only for the main server, you can use a
C<s-E<gt>is_virtual> conditional. For example we need to enable the
debug tracing as early as possible, but we need the server object in
order to do that, so we perform this setting in
C<modperl_config_srv_create()>:
if (!s->is_virtual) {
modperl_trace_level_set(s, NULL);
}
=head1 The pre_config stage
After Apache processes its command line arguments, creates various
pools and reads the configuration file in, it runs the registered
I<pre_config> hooks by calling C<ap_run_pre_config()>. That's when
C<modperl_hook_pre_config> is called. And it does nothing.
=head2 Configuration Tree Processing
C<ap_process_config_tree> calls C<ap_walk_config>, which scans through
all directives in the parsed configuration tree, and executes each one
by calling C<ap_walk_config_sub>. This is a recursive process with
many twists.
Similar to C<ap_build_config_sub> for each command (directive) in the
configuration tree, it calls C<ap_find_command_in_modules> to find a
module that registered that command. If the command is not found the
server dies. Otherwise the callback for that command is invoked with
C<invoke_cmd>, after fetching the current directory configuration:
invoke_cmd(cmd, parms, dir_config, current->args);
The C<invoke_cmd> command is the one that invokes mod_perl's
directives callbacks, which reside in I<modperl_cmd.c>. C<invoke_cmd>
knows how the arguments should be passed to the callbacks, based on
the information in the C<modperl_cmds> array that we have just
mentioned.
Notice that before C<invoke_cmd> is invoked,
C<ap_set_config_vectors()> is called which sets the current server and
section configuration objects for the module in which the directive
has been found. If these objects were't created yet, it calls the
registered callbacks as C<create_dir_config> and
C<create_server_config>, which are C<modperl_config_dir_create> and
C<modperl_config_srv_create> for the mod_perl module. (If you write
your L<custom module in Perl|docs::2.0::user::config::custom>, these
correspond to the C<DIR_CREATE> and C<SERVER_CREATE> Perl
subroutines.)
The command callback won't be invoked if it has the C<EXEC_ON_READ>
flag set, because it was already invoked earlier when the
configuration tree was parsed. C<ap_set_config_vectors()> is called in
any case, because it wasn't called during the C<ap_build_config>.
So we have C<modperl_config_srv_create> and
C<modperl_config_dir_create> both called once for the main server (at
the end of processing the C<LoadModule perl_module ...> directive),
and one more time for each virtual host in which at least one mod_perl
directive is encountered. In addition C<modperl_config_dir_create> is
called for every section and subsection that includes mod_perl
directives (META: or inherits from such a section even though
specifies no mod_perl directives in it?).
=head1 Request Processing
META: need to write
=head1 Shutdown
META: need to write
=head1 Maintainers
Maintainer is the person(s) you should contact with updates,
corrections and patches.
=over
=item *
Stas Bekman E<lt>stas (at) stason.orgE<gt>
=back
=head1 Authors
=over
=item *
=back
Only the major authors are listed above. For contributors see the
Changes file.
=cut
1.1 modperl-docs/src/docs/2.0/devel/core/coding_style.pod
Index: coding_style.pod
===================================================================
=head1 NAME
mod_perl Coding Style Guide
=head1 Description
This document explains the coding style used in the core mod_perl
development and which should be followed by all core developers.
=head1 Coding Style Guide
We try hard to code mod_perl using an identical style. Because
everyone in the team should be able to read and understand the code as
quickly and easily as possible. Some will have to adjust their habits
for the benefit of all.
=over 4
=item * C code
mod_perl's C code follows the Apache style guide:
http://dev.apache.org/styleguide.html
=item * XS code
C code inside XS modules also follows the Apache style guide.
=item * Perl code
mod_perl's Perl code also follows the Apache style guide, in terms of
indentation, braces, etc. Style issues not covered by Apache style of
guide should be looked up in the I<perlstyle> manpage.
=back
Here are the rough guidelines with more stress on the Perl coding
style.
=over 4
=item Indentation and Tabs
Do use 4 characters indentation.
Do NOT use tabs.
Here is how to setup your editor to do the right thing:
=over
=item * x?emacs: cperl-mode
.xemacs/custom.el:
------------------
(custom-set-variables
'(cperl-indent-level 4)
'(cperl-continued-statement-offset 4)
'(cperl-tab-always-indent t)
'(indent-tabs-mode nil)
)
=item * vim
.vimrc:
-------
set expandtab " replaces any tab keypress with the appropriate number of
spaces
set tabstop=4 " sets tabs to 4 spaces
=back
=item Block Braces
Do use a style similar to K&R style, not the same. The following
example is the best guide:
Do:
sub foo {
my($self, $cond, $baz, $taz) = @_;
if ($cond) {
bar();
}
else {
$self->foo("one", 2, "...");
}
return $self;
}
Don't:
sub foo
{
my ($self,$bar,$baz,$taz)[EMAIL PROTECTED];
if( $cond )
{
&bar();
} else { $self->foo ("one",2,"..."); }
return $self;
}
=item Lists and Arrays
Whenever you create a list or an array, always add a comma after the
last item. The reason for doing this is that it's highly probable that
new items will be appended to the end of the list in the future. If
the comma is missing and this isn't noticed, there will be an error.
Do:
my @list = (
"item1",
"item2",
"item3",
);
Don't:
my @list = (
"item1",
"item2",
"item3"
);
=item Last Statement in the Block
The same goes for C<;> in the last statement of the block. Almost
always add it even if it's not required, so when you add a new
statement you don't have to remember to add C<;> on a previous line.
Do:0
sub foo {
statement1;
statement2;
statement3;
}
Don't
sub foo {
statement1;
statement2;
statement3
}
=back
=head1 Function and Variable Prefixes Convention
=over 4
=item modperl_
The prefix for mod_perl C API functions.
=item MP_
The prefix for mod_perl C macros.
=item mpxs_
The prefix for mod_perl XS utility functions.
=item mp_xs_
The prefix for mod_perl I<generated> XS utility functions.
=item MPXS_
The prefix for mod_perl XSUBs with an XS() prototype.
=back
=head1 Coding Guidelines
The following are the Perl coding guidelines:
=head2 Global Variables
=over 4
=item avoid globals in general
=item avoid $&, $', $`
See C<Devel::SawAmpersand>'s I<README> that explains the evilness.
Under mod_perl everybody suffers when one is seen anywhere since the
interpreter is never shutdown.
=back
=head2 Modules
=over 4
=item Exporting/Importing
Avoid too much exporting/importing (glob aliases eat up memory)
When you do wish to import from a module try to use an explicit list or
tag whenever possible, e.g.:
use POSIX qw(strftime);
When you do not wish to import from a module, always use an empty list
to avoid any import, e.g.:
use IO::File ();
(explain how to use Apache::Status to find imported/exported
functions)
=back
=head2 Methods
=over 4
=item indirect vs direct method calls
Avoid indirect method calls, e.g.
Do:
CGI::Cookie->new
Don't:
new CGI::Cookie
=back
=head2 Inheritance
=over 4
=item Avoid inheriting from certain modules
Exporter.
To avoid inheriting B<AutoLoader::AUTOLOAD>
Do:
*import = \&Exporter::import;
Don't:
@MyClass::ISA = qw(Exporter);
=back
=head2 Symbol tables
=over 4
=item %main::
stay away from C<main::> to avoid namespace clashes
=back
=head2 Use of $_ in loops
Avoid using C<$_> in loops unless it's a short loop and you don't call
any subs from within the loop. If the loop started as short and then
started to grow make sure to remove the use of C<$_>:
Do:
for my $idx (1..100) {
....more than few lines...
foo($idx);
....
}
Don't:
for (1..100) {
....more than a few statements...
foo();
....
}
Because foo() might change C<$_> if foo()'s author didn't localize C<$_>.
This is OK:
for (1..100) {
.... a few statements with no subs called
# do something with $_
....
}
=head1 Maintainers
Maintainer is the person(s) you should contact with updates,
corrections and patches.
Stas Bekman E<lt>stas *at* stason.orgE<gt>
=head1 Authors
=over
=item *
Doug MacEachernE<lt>dougm (at) covalent.netE<gt>
=item *
Stas Bekman E<lt>stas (at) stason.orgE<gt>
=back
Only the major authors are listed above. For contributors see the
Changes file.
=cut
1.1 modperl-docs/src/docs/2.0/devel/core/explained.pod
Index: explained.pod
===================================================================
=head1 NAME
mod_perl 2.0 Source Code Explained
=head1 Description
This document explains how to navigate the mod_perl source code,
modify and rebuild the existing code and most important: how to add
new functionality.
=head1 Project's Filesystem Layout
In its pristine state the project is comprised of the following
directories and files residing at the root directory of the project:
Apache-Test/ - test kit for mod_perl and Apache::* modules
ModPerl-Registry/ - ModPerl::Registry sub-project
build/ - utilities used during project build
docs/ - documentation
lib/ - Perl modules
src/ - C code that builds libmodperl.so
t/ - mod_perl tests
todo/ - things to be done
util/ - useful utilities for developers
xs/ - source xs code and maps
Changes - Changes file
LICENSE - ASF LICENSE document
Makefile.PL - generates all the needed Makefiles
After building the project, the following root directories and files
get generated:
Makefile - Makefile
WrapXS/ - autogenerated XS code
blib/ - ready to install version of the package
=head1 Directory src
=head2 Directory src/modules/perl/
The directory I<src/modules/perl> includes the C source files needed
to build the I<libmodperl> library.
Notice that several files in this directory are autogenerated during
the I<perl Makefile> stage.
When adding new source files to this directory you should add their
names to the C<@c_src_names> variable in I<lib/ModPerl/Code.pm>, so
they will be picked up by the autogenerated I<Makefile>.
=head1 Directory xs/
Apache/ - Apache specific XS code
APR/ - APR specific XS code
ModPerl/ - ModPerl specific XS code
maps/ -
tables/ -
Makefile.PL -
modperl_xs_sv_convert.h -
modperl_xs_typedefs.h -
modperl_xs_util.h -
typemap -
=head2 xs/Apache, xs/APR and xs/ModPerl
The I<xs/Apache>, I<xs/APR> and I<xs/ModPerl> directories include I<.h> files
which
have C and XS code in them. They all have the I<.h> extension because
they are always C<#include-d>, never compiled into their own object
file. and only the file that C<#include-s> an I<.h> file from these
directories should be able to see what's in there. Anything else
belongs in a I<src/modules/perl/foo.c> public API.
=head2 xs/maps
The I<xs/maps> directory includes mapping files which describe how
Apache Perl API should be constructed and various XS typemapping.
These files get modified whenever:
=over
=item *
a new function is added or the API of the existing one is modified.
=item *
a new struct is added or the existing one is modified
=item *
a new C datatype or Perl typemap is added or an existing one is
modified.
=back
The execution of:
% make source_scan
or:
% perl build/source_scan.pl
converts these map files into their Perl table representation in the
I<xs/tables/current/> directory. This Perl representation is then used
during C<perl Makefile.PL> to generate the XS code in the I<./WrapXS/>
directory by the xs_generate() function. This XS code is combined of
the Apache API Perl glue and mod_perl specific extensions.
NOTE: source_scan requires C::Scan 0.75, which at the moment is
unreleased, there is a working copy here:
http://perl.apache.org/~dougm/Scan.pm
If you need to skip certain unwanted C defines from being picked by
the source scanning you can add them to the array
C<$Apache::ParseSource::defines_unwanted> in
I<lib/Apache/ParseSource.pm>.
Notice that I<source_scan> target is normally not run during the
project build process, since the source scanning is not stable yet,
therefore everytime the map files change, C<make source_scan> should
be run manually and the updated files ending up in the
I<xs/tables/current/> directory should be committed to the cvs
repository.
The I<source_scan> make target is actually to run
I<build/source_scan.pl>, which can be run directly without needing to
create I<Makefile> first.
There are three different types of map files in the I<xs/maps/>
directory:
=over
=item * Functions Mapping
apache_functions.map
modperl_functions.map
apr_functions.map
=item * Structures Mapping
apache_structures.map
apr_structures.map
=item * Types Mapping
apache_types.map
apr_types.map
modperl_types.map
=back
The following sections describe the syntax of the files in each group
=head3 Functions Mapping
The functions mapping file is comprised of groups of function
definitions. Each group starts with a header similar to XS syntax:
MODULE=... PACKAGE=... PREFIX=... BOOT=... ISA=...
where:
=over
=item * C<MODULE>
the module name where the functions should be put. e.g. C<MODULE
Apache::Connection> will place the functions into
I<WrapXS/Apache/Connection.{pm,xs}>.
=item * C<PACKAGE>
the package name functions belong to, defaults to C<MODULE>. The
value of I<guess> indicates that package name should be guessed based
on first argument found that maps to a Perl class. If the value is
not defined and the function's name starts with I<ap_> the C<Apache>
package will be used, if it starts with I<apr_> then the C<APR>
package is used.
=item * C<PREFIX>
prefix string to be stripped from the function name. If not specified
it defaults to C<PACKAGE>, converted to C name convention, e.g.
C<APR::Base64> makes the prefix: I<apr_base64_>. If the converted
prefix does not match, defaults to I<ap_> or I<apr_>.
=item * C<BOOT>
The C<BOOT> directive tells the XS generator, whether to add the boot
function to the autogenerated XS file or not. If the value of C<BOOT>
is not true or it's simply not declared, the boot function won't be
added.
If the value is true, a boot function will be added to the XS file.
Note, that this function is not declared in the map file.
The boot function name must be constructed from three parts:
'mpxs_' . MODULE . '_BOOT'
where C<MODULE> is the one declared with C<MODULE=> in the map file.
For example if we want to have an XS boot function for a class
C<APR::IO>, we create this function in I<xs/APR/IO/APR__IO.h>:
static void mpxs_APR__IO_BOOT(pTHX)
{
/* boot code here */
}
and now we add the C<BOOT=1> declaration to the
I<xs/maps/modperl_functions.map> file:
MODULE=APR::IO PACKAGE=APR::IO BOOT=1
Notice that the C<PACKAGE=> declaration is a must.
When I<make xs_generate> is run (after running I<make source_scan>),
it autogenerates I<Wrap/APR/IO/IO.xs> and amongst other things will
include:
BOOT:
mpxs_APR__IO_BOOT(aTHXo);
=item * C<ISA>
META: complete
=back
Every function definition is declared on a separate line (use C<\> if
the line is too long), using the following format:
C function name | Dispatch function name | Argspec | Perl alias
where:
=over
=item * C function name
The name of the real C function.
Function names that do not begin with C</^\w/> are skipped. For
details see: C<%ModPerl::MapUtil::disabled_map>.
The return type can be specified before the C function name. It
defaults to I<return_type> in C<{Apache,ModPerl}::FunctionTable>.
META: DEFINE nuances
=item * Dispatch function name
Dispatch function name defaults to C function name. If the dispatch
name is just a prefix (I<mpxs_>, I<MPXS_>) the C function name is
appended to it.
See the explanation about function naming and arguments passing.
=item * Argspec
The argspec defaults to arguments in
C<{Apache,ModPerl}::FunctionTable>. Argument types can be specified
to override those in the C<FunctionTable>. Default values can be
specified, e.g. C<arg=default_value>. Argspec of C<...> indicates
I<passthru>, calling the function with C<(aTHX_ I32 items, SP **sp, SV
**MARK)>.
=item * Perl alias
the Perl alias will be created in the current C<PACKAGE>.
=back
=head3 Structures Mapping
META: complete
=head3 Types Mapping
META: complete
=head3 Modifying Maps
As explained in the beginning of this section, whenever the map file
is modified you need first to run:
% make source_scan
Next check that the conversion to Perl tables is properly done by
verifying the resulting corresponding file in
I<xs/tables/current>. For example I<xs/maps/modperl_functions.map> is
converted into I<xs/tables/current/ModPerl/FunctionTable.pm>.
If you want to do a visual check on how XS code will be generated, run:
% make xs_generate
and verify that the autogenerated XS code under the directory
I<./WrapXS> is correct. Otherwise build the project normally:
% perl Makefile.PL ...
=head2 XS generation process
As mentioned before XS code is generated in the I<WrapXS> directory
either during C<perl Makefile.PL> via xs_generate() if
C<MP_GENERATE_XS=1> is used (which is the default) or explicitly via:
% make xs_generate
In addition it creates a number of files in the I<xs/> directory:
modperl_xs_sv_convert.h
modperl_xs_typedefs.h
=head1 Gluing Existing APIs
If you have an API that you simply want to provide the Perl interface
without writing any code...
META: complete
WrapXS allows you to adjust some arguments and supply default values
for function arguments without writing any code
META: complete
C<MPXS_> functions are final C<XSUBs> and always accept:
aTHX_ I32 items, SP **sp, SV **MARK
as their arguments. Whereas C<mpxs_> functions are either intermediate
thin wrappers for the existing C functions or functions that do
something by themselves. C<MPXS_> functions also can be used for
writing thin wrappers for C macros.
=head1 Adding Wrappers for existing APIs and Creating New APIs
In certain cases the existing APIs need to be adjusted. There are a
few reasons for doing this.
First, is to make the given C API more Perlish. For example C
functions cannot return more than one value, and the pass by reference
technique is used. This is not Perlish. Perl has no problem returning
a list of value, and passing by reference is used only when an array
or a hash in addition to any other variables need to be passes or
returned from the function. Therefore we may want to adjust the C API
to return a list rather than passing a reference to a return value,
which is not intuitive for Perl programmers.
Second, is to adjust the functionality, i.e. we still use the C API
but may want to adjust its arguments before calling the original
function, or do something with return values. And of course optionally
adding some new code.
Third, is to create completely new APIs. It's quite possible that we
need more functionality built on top of the existing API. In that case
we simply create new APIs.
The following sections discuss various techniques for retrieving
function arguments and returning values to the caller. They range from
using usual C argument passing and returning to more complex Perl
arguments' stack manipulation. Once you know how to retrieve the
arguments in various situations and how to put the return values on
the stack, the rest is usually normal C programming potentially
involving using Perl APIs.
Let's look at various ways we can declare functions and what options
various declarions provide to us:
=head2 Functions Returning a Single Value (or Nothing)
If its know deterministically what the function returns and there is
only a single return value (or nothing is returned == I<void>), we are
on the C playground and we don't need to manipulate the returning
stack. However if the function may return a single value or nothing
at all, depending on the inputs and the code, we have to manually
manipulate the stack and therefore this section doesn't apply.
Let's look at various requirements and implement these using simple
examples. The following testing code exercises the interfaces we are
about to develop, so refer to this code to see how the functions are
invoked from Perl and what is returned:
file:t/response/TestApache/coredemo.pm
----------------------------------------
package TestApache::coredemo;
use strict;
use warnings FATAL => 'all';
use Apache::Const -compile => 'OK';
use Apache::Test;
use Apache::TestUtil;
use Apache::CoreDemo;
sub handler {
my $r = shift;
plan $r, tests => 7;
my $a = 7;
my $b = 3;
my ($add, $subst);
$add = Apache::CoreDemo::print($a, $b);
t_debug "print";
ok !$add;
$add = Apache::CoreDemo::add($a, $b);
ok t_cmp($a + $b, $add, "add");
$add = Apache::CoreDemo::add_sv($a, $b);
ok t_cmp($a + $b, $add, "add: return sv");
$add = Apache::CoreDemo::add_sv_sv($a, $b);
ok t_cmp($a + $b, $add, "add: pass/return svs");
($add, $subst) = @{ Apache::CoreDemo::add_subst($a, $b) };
ok t_cmp($a + $b, $add, "add_subst: add");
ok t_cmp($a - $b, $subst, "add_subst: subst");
$subst = Apache::CoreDemo::subst_sp($a, $b);
ok t_cmp($a - $b, $subst, "subst via SP");
Apache::OK;
}
1;
The first case is the simplest: pass two integer arguments, print
these to the STDERR stream and return nothing:
file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
void mpxs_Apache__CoreDemo_print(int a, int b)
{
fprintf(stderr, "%d, %d\n", a, b);
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
mpxs_Apache__CoreDemo_print
Now let's say that the I<b> argument is optional and in case it wasn't
provided, we want to use a default value, e.g. 0. In that case we
don't need to change the code, but simply adjust the map file to be:
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
mpxs_Apache__CoreDemo_print | | a, b=0
In the previous example, we didn't list the arguments in the map file
since they were automatically retrieved from the source code. In this
example we tell WrapXS to assign a value of C<0> to the argument b, if
it wasn't supplied by the caller. All the arguments must be listed and
in the same order as they are defined in the function.
You may add an extra test that test teh default value assignment:
$add = Apache::CoreDemo::add($a);
ok t_cmp($a + 0, $add, "add (b=0 default)");
The second case: pass two integer arguments and return their sum:
file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
int mpxs_Apache__CoreDemo_add(int a, int b)
{
return a + b;
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
mpxs_Apache__CoreDemo_add
The third case is similar to the previous one, but we return the sum
as as a Perl scalar. Though in C we say SV*, in the Perl space we will
get a normal scalar:
file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
SV *mpxs_Apache__CoreDemo_add_sv(pTHX_ int a, int b)
{
return newSViv(a + b);
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
mpxs_Apache__CoreDemo_add_sv
In the second example the XSUB function was converting the returned
I<int> value to a Perl scalar behind the scenes. In this example we
return the scalar ourselves. This is of course to demonstrate that you
can return a Perl scalar, which can be a reference to a complex Perl
datastructure, which we will see in the fifth example.
The forth case demonstrates that you can pass Perl variables to your
functions without needing XSUB to do the conversion. In all previous
examples XSUB was automatically converting Perl scalars in the
argument list to the corresponding C variables, using the typemap
definitions.
file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
SV *mpxs_Apache__CoreDemo_add_sv_sv(pTHX_ SV *a_sv, SV *b_sv)
{
int a = (int)SvIV(a_sv);
int b = (int)SvIV(b_sv);
return newSViv(a + b);
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
mpxs_Apache__CoreDemo_add_sv_sv
So this example is the same simple case of addition, though we
manually convert the Perl variables to C variables, perform the
addition operation, convert the result to a Perl Scalar of kind I<IV>
(Integer Value) and return it directly to the caller.
In case where more than one value needs to be returned, we can still
implement this without directly manipulating the stack before a
function returns. The fifth case demonstrates a function that returns
the result of addition and substruction operations on its arguments:
file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
SV *mpxs_Apache__CoreDemo_add_subst(pTHX_ int a, int b)
{
AV *av = newAV();
av_push(av, newSViv(a + b));
av_push(av, newSViv(a - b));
return newRV_noinc((SV*)av);
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
mpxs_Apache__CoreDemo_add_subst
If you look at the corresponding testing code:
($add, $subst) = @{ Apache::CoreDemo::add_subst($a, $b) };
ok t_cmp($a + $b, $add, "add_subst: add");
ok t_cmp($a - $b, $subst, "add_subst: subst");
you can see that this technique comes at a price of needing to
dereference the return value to turn it into a list. The actual code
is very similar to the C<Apache::CoreDemo::add_sv> function which
was doing only the addition operation and returning a Perl
scalar. Here we perform the addition and the substraction operation
and push the two results into a previously created I<AV*> data
structure, which represents an array. Since only the I<SV>
datastructures are allowed to be put on stack, we take a reference
I<RV> (which is of an I<SV> kind) to the existing I<AV> and return it.
The sixth case demonstrates a situation where the number of arguments
or their types may vary and aren't known at compile time. Though
notice that we still know that we are returning at compile time (zero
or one arguments), I<int> in this example:
file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static MP_INLINE
int mpxs_Apache__CoreDemo_subst_sp(pTHX_ I32 items, SV **MARK, SV **SP)
{
int a, b;
if (items != 2) {
Perl_croak(aTHX_ "usage: ...");
}
a = mp_xs_sv2_int(*MARK);
b = mp_xs_sv2_int(*(MARK+1));
return a - b;
}
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
mpxs_Apache__CoreDemo_subst_sp | | ...
In the map file we use a special token C<...> which tells the XSUB
constructor to pass C<items>, C<MARK> and C<SP> arguments to the
function. The macro C<MARK> points to the first argument passed by the
caller in the Perl namespace. For example to access the second
argument to retrieve the value of C<b> we use C<*(MARK+1)>, which if
you remember represented as an I<SV> variable, which nees to be
converted to the corresponding C type.
In this example we use the macro I<mp_xs_sv2_int>, automatically
generated based on the data from the I<xs/typemap> and
I<xs/maps/*_types.map> files, and placed into the
I<xs/modperl_xs_sv_convert.h> file. In the case of I<int> C type the
macro is:
#define mp_xs_sv2_int(sv) (int)SvIV(sv)
which simply converts the I<SV> variable on the stack and generates an
I<int> value.
While in this example you have an access to the stack, you cannot
manipulate the return values, because the XSUB wrapper expects a
single return value of type I<int>, so even if you put something on
the stack it will be ignored.
=head2 Functions Returning Variable Number of Values
We saw earlier that if we want to return an array one of the ways to
go is to return a reference to an array as a single return value,
which fits the C paradigm. So we simply declare the return value as
C<SV*>.
This section talks about cases where it's unknown at compile time how
many return values will be or it's known that there will be more than
one return value--something that C cannot handle via its return
mechanism.
Let's rewrite the function C<mpxs_Apache__CoreDemo_add_subst> from
the earlier section to return two results instead of a reference to a
list:
file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
static XS(MPXS_Apache__CoreDemo_add_subst_sp)
{
dXSARGS;
int a, b;
if (items != 2) {
Perl_croak(aTHX_ "usage: Apache::CoreDemo::add_subst_sp($a, $b)");
}
a = mp_xs_sv2_int(ST(0));
b = mp_xs_sv2_int(ST(1));
SP -= items;
if (GIMME == G_ARRAY) {
EXTEND(sp, 2);
PUSHs(sv_2mortal(newSViv(a + b)));
PUSHs(sv_2mortal(newSViv(a - b)));
}
else {
XPUSHs(sv_2mortal(newSViv(a + b)));
}
PUTBACK;
}
Before explaining the function here is the prototype we add to the map
file:
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
DEFINE_add_subst_sp | MPXS_Apache__CoreDemo_add_subst_sp | ...
The C<mpxs_> functions declare in the third column the arguments that
they expect to receive (and optionally the default values). The
C<MPXS> functions are the real C<XSUBs> and therefore they always
accept:
aTHX_ I32 items, SP **sp, SV **MARK
as their arguments. Thefore it doesn't matter what is placed in this
column when the C<MPXS_> function is declared. Usually for
documentation the Perl side arguments are listed. For example you can
say:
DEFINE_add_subst_sp | MPXS_Apache__CoreDemo_add_subst_sp | x, y
In this function we manually manipulate the stack to retrieve the
arguments passed on the Perl side and put the results back onto the
stack. Therefore the first thing we do is to initialize a few special
variables using the C<dXSARGS> macro defined in I<XSUB.h>, which in
fact calls a bunch of other macros. These variables help to manipulate
the stack. C<dSP> is one of these macros and it declares and initial�
izes a local copy of the Perl stack pointer C<sp> which . This local
copy should always be accessed as C<SP>.
We retrieve the original function arguments using the C<ST()>
macros. C<ST(0)> and C<ST(1)> point to the first and the second
argument on the stack, respectively. But first we check that we have
exactly two arguments on the stack, and if not we abort the
function. The C<items> variable is the function argument.
Once we have retrieved all the arguments from the stack we set the
local stack pointer C<SP> to point to the bottom of the stack (like
there are no items on the stack):
SP -= items;
Now we can do whatever processing is needed and put the results back
on the stack. In our example we return the results of addition and
substraction operations if the function is called in the list
context. In the scalar context the function returns only the result of
the addition operation. We use the C<GIMME> macro which tells us the
context.
In the list context we make sure that we have two spare slots on the
stack since we are going to push two items, and then we push them
using the C<PUSHs> macro:
EXTEND(sp, 2);
PUSHs(sv_2mortal(newSViv(a + b)));
PUSHs(sv_2mortal(newSViv(a - b)));
Alternatively we could use:
XPUSHs(sv_2mortal(newSViv(a + b)));
XPUSHs(sv_2mortal(newSViv(a - b)));
The C<XPUSHs> macro eI<X>tends the stack before pushing the item into
it if needed. If we plan to push more than a single item onto the
stack, it's more efficient to extend the stack in one call.
In the scalar context we push only one item, so here we use the
C<XPUSHs> macro:
XPUSHs(sv_2mortal(newSViv(a + b)));
The last command we call is:
PUTBACK;
which makes the local stack pointer global. This is a must call if the
state of the stack was changed when the function is about to
return. The stack changes if something was popped from or pushed to
it, or both and changed the number of items on the stack.
In our example we don't need to call C<PUTBACK> if the function is
called in the list context. Because in this case we return two
variables, the same as two function arguments, the count didn't
change. Though in the scalar context we push onto the stack only one
argument, so the function won't return what is expected. The simplest
way to avoid errors here is to always call C<PUTBACK> when the stack
is changed.
For more information refer to the I<perlcall> manpage which explains
the stack manipulation process in great details.
Finally we test the function in the list and scalar contexts:
file:t/response/TestApache/coredemo.pm
----------------------------------------
...
my $a = 7;
my $b = 3;
my ($add, $subst);
# list context
($add, $subst) = Apache::CoreDemo::add_subst_sp($a, $b);
ok t_cmp($a + $b, $add, "add_subst_sp list context: add");
ok t_cmp($a - $b, $subst, "add_subst_sp list context: subst");
# scalar context
$add = Apache::CoreDemo::add_subst_sp($a, $b);
ok t_cmp($a + $b, $add, "add_subs_spt scalar context: add");
...
=head2 Wrappers Functions for C Macros
Let's say you have a C macro which you want to provide a Perl
interface for. For example let's take a simple macro which performs
the power of function:
file:xs/Apache/CoreDemo/Apache__CoreDemo.h
----------------------------------------------
#define mpxs_Apache__CoreDemo_power(x, y) pow(x, y)
To create the XS glue code we use the following entry in the map file:
file:xs/maps/modperl_functions.map
----------------------------------
MODULE=Apache::CoreDemo
double:DEFINE_power | | double:x, double:y
This works very similar to the C<MPXS_Apache__CoreDemo_add_subst_sp>
function presented earlier. But since this is a macro the XS wrapper
needs to know the types of the arguments and the return type, so these
are added. The return type is added just before the function name and
separated from it by the colon (C<:>), the argument types are
specified in the third column. The type is always separated from the
name of the variable by the colon (C<:>).
And of course finally we need to test that the function works in Perl:
file:t/response/TestApache/coredemo.pm
----------------------------------------
...
my $a = 7;
my $b = 3;
my $power = Apache::CoreDemo::power($a, $b);
ok t_cmp($a ** $b, $power, "power macro");
...
=head1 Wrappers for modperl_, apr_ and ap_ APIs
If you already have a C function whose name starts from I<modperl_>,
I<apr_> or I<ap_> and you want to do something before calling the real
C function, you can write a XS wrapper using the same method as in the
L<MPXS_Apache__CoreDemo_add_subst_sp
|/Functions_Returning_Variable_Number_of_Values>. The only difference
is that it'll be clearly seen in the map file that this is a wrapper
for an existing C API.
Let's say that we have an existing C function apr_power(), this is how
we declare its wrapper:
file:xs/maps/apr_functions.map
----------------------------------
MODULE=APR::Foo
apr_power | MPXS_ | x, y
The first column specifies the existing function's name, the second
tells that the XS wrapper will use the C<MPXS_> prefix, which means
that the wrapper must be called C<MPXS_apr_power>. The third column
specifies the argument names, but for C<MPXS_> no matter what you
specify there the C<...> will be passed:
aTHX_ I32 items, SP **sp, SV **MARK
so you can leave that column empty, but here we use C<x> and C<y> to
remind us that these two arguments are passed from Perl.
If the forth column is empty this function will be called
C<APR::Foo::power> in the Perl namespace. But you can use that column
to give a different Perl name, e.g with:
apr_power | MPXS_ | x, y | pow
This function will be available from Perl as C<APR::Foo::pow>.
Similarly you can write a C<MPXS_modperl_power> wrapper for a
C<modperl_power()> function but here you have to explicitly give the
Perl function's name in the forth column:
file:xs/maps/apr_functions.map
----------------------------------
MODULE=Apache::CoreDemo
modperl_power | MPXS_ | x, y | mypower
and the Perl function will be called C<Apache::CoreDemo::mypower>.
The C<MPXS_> wrapper's implementation is similar to
L<MPXS_Apache__CoreDemo_add_subst_sp
|/Functions_Returning_Variable_Number_of_Values>.
=head1 MP_INLINE vs C Macros vs Normal Functions
To make the code maintainable and reusable functions and macros are
used in when programming in C (and other languages :).
When function is marked as I<inlined> it's merely a hint to the
compiler to replace the call to a function with the code inside this
function (i.e. inlined). Not every function can be inlined. Some
typical reasons why inlining is sometimes not done include:
=over
=item *
the function calls itself, that is, is recursive
=item *
the function contains loops such as C<for(;;)> or C<while()>
=item *
the function size is too large
=back
Most of the advantage of inline functions comes from avoiding the
overhead of calling an actual function. Such overhead includes saving
registers, setting up stack frames, etc. But with large functions the
overhead becomes less important.
Use the C<MP_INLINE> keyword in the declaration of the functions that
are to be inlined. The functions should be inlined when:
=over
=item *
Only ever called once (the I<wrappers> that are called from I<.xs>
files), no matter what the size of code is.
=item *
Short bodies of code called in a I<hot> code (like
I<modperl_env_hv_store>, which is called many times inside of a loop),
where it is cleaner to see the code in function form rather than macro
with lots of C<\>'s. Remember that an inline function takes much more
space than a normal functions if called from many places in the code.
=back
Of course C macros are a bit faster then inlined functions, since
there is not even I<short jump> to be made, the code is literally
copied into the place it's called from. However using macros comes at
a price:
=over
=item *
Also unlike macros, in functions argument types are checked, and
necessary conversions are performed correctly. With macros it's
possible that weird things will happen if the caller has passed
arguments of the wrong type when calling a macro.
=item *
One should be careful to pass only absolute values as I<"arguments">
to macros. Consider a macro that returns an absolute value of the
passed argument:
#define ABS(v) ( (v) >= 0 ? (v) : -(v) )
In our example if you happen to pass a function it will be called
twice:
abs_val = ABS(f());
Since it'll be extended as:
abs_val = f() >= 0 ? f() : -f();
You cannot do simple operation like increment--in our example it will
be called twice:
abs_val = ABS(i++);
Because it becomes:
abs_val = i++ >= 0 ? i++ : -i++;
=item *
It's dangerous to use the if() condition without enclosing the code in
C<{}>, since the macro may be called from inside another if-else
condition, which may cause the else part called if the if() part from
the macro fails.
But we always use C<{}> for the code inside the if-else condition, so
it's not a problem here.
=item *
A multi-line macro can cause problems if someone uses the macro in a
context that demands a single statement.
while (foo) MYMACRO(bar);
But again, we always enclose any code in conditional with C<{}>, so
it's not a problem for us.
=item *
Inline functions present a problem for debuggers and profilers,
because the function is expanded at the point of call and loses its
identity. This makes the debugging process a nightmare.
A compiler will typically have some option available to disable
inlining.
=back
In all other cases use normal functions.
=head1 Adding New Interfaces
=head2 Adding Typemaps for new C Data Types
Sometimes when a new interface is added it may include C data types
for which we don't have corresponding XS typemaps yet. In such a case,
the first thing to do is to provide the required typemaps.
Let's add a prototype for the I<typedef struct scoreboard> data type
defined in I<httpd-2.0/include/scoreboard.h>.
First we include the relevant header files in
I<src/modules/perl/modperl_apache_includes.h>:
#include "scoreboard.h"
If you want to specify your own type and don't have a header file for
it (e.g. if you extend some existing datatype within mod_perl) you may
add the I<typedef> to I<src/modules/perl/modperl_types.h>.
After deciding that C<Apache::Scoreboard> is the Perl class will be
used for manipulating C I<scoreboard> data structures, we map the
I<scoreboard> data structure to the C<Apache::Scoreboard>
class. Therefore we add to I<xs/maps/apache_types.map>:
struct scoreboard | Apache::Scoreboard
Since we want the I<scoreboard> data structure to be an opaque object
on the perl side, we simply let mod_perl use the default C<T_PTROBJ>
typemap. After running C<make xs_generate> you can check the assigned
typemap in the autogenerated I<WrapXS/typemap> file.
If you need to do some special handling while converting from C to
Perl and back, you need to add the conversion functions to the
I<xs/typemap> file. For example the C<Apache::RequestRec> objects need
special handling, so you can see the special C<INPUT> and C<OUTPUT>
typemappings for the corresponding C<T_APACHEOBJ> object type.
Now we run C<make xs_generate> and find the following definitions in
the autogenerated files:
file:xs/modperl_xs_typedefs.h
-----------------------------
typedef scoreboard * Apache__Scoreboard;
file:xs/modperl_xs_sv_convert.h
-------------------------------
#define mp_xs_sv2_Apache__Scoreboard(sv) \
((SvROK(sv) && (SvTYPE(SvRV(sv)) == SVt_PVMG)) \
|| (Perl_croak(aTHX_ "argument is not a blessed reference \
(expecting an Apache::Scoreboard derived object)"),0) ? \
(scoreboard *)SvIV((SV*)SvRV(sv)) : (scoreboard *)NULL)
#define mp_xs_Apache__Scoreboard_2obj(ptr) \
sv_setref_pv(sv_newmortal(), "Apache::Scoreboard", (void*)ptr)
The file I<xs/modperl_xs_typedefs.h> declares the typemapping from C
to Perl and equivalent to the C<TYPEMAP> section of the XS's
I<typemap> file. The second file I<xs/modperl_xs_sv_convert.h>
generates two macros. The first macro is used to convert from Perl to
C datatype and equivalent to the I<typemap> file's C<INPUT>
section. The second macro is used to convert from C to Perl datatype
and equivalent to the I<typemap>'s C<OUTPUT> section.
Now proceed on adding the glue code for the new interface.
=head2 Importing Constants and Enums into Perl API
To I<import> httpd and APR constants and enums into Perl API, edit
I<lib/Apache/ParseSource.pm>. To add a new type of C<DEFINE> constants
adjust the C<%defines_wanted> variable, for C<enums> modify
C<%enums_wanted>.
For example to import all C<DEFINE>s starting with C<APR_FLOCK_> add:
my %defines_wanted = (
...
APR => {
...
flock => [qw{APR_FLOCK_}],
...
},
);
When deciding which constants are to be exported, the regular
expression will be used, so in our example all matches
C</^APR_FLOCK_/> will be imported into the Perl API.
For example to import an I<read_type_e> C<enum> for APR, add:
my %enums_wanted = (
APR => { map { $_, 1 } qw(apr_read_type) },
);
Notice that I<_e> part at the end of the enum name has gone.
After adding/modifying the datastructures make sure to run C<make
source_scan> or C<perl build/source_scan.pl> and verify that the
wanted constant or enum were picked by the source scanning
process. Simply grep I<xs/tables/current> for the wanted string. For
example after adding I<apr_read_type_e> enum we can check:
% more xs/tables/current/Apache/ConstantsTable.pm
...
'read_type' => [
'APR_BLOCK_READ',
'APR_NONBLOCK_READ'
],
Of course the newly added constant or enum's typemap should be
declared in the appropriate I<xs/maps/*_types.map> files, so the XS
conversion of arguments will be performed correctly. For example
I<apr_read_type> is an APR enum so it's declared in
I<xs/maps/apr_types.map>:
apr_read_type | IV
C<IV> is used as a typemap, Since enum is just an integer. In more
complex cases the typemap can be different. (META: examples)
=head1 Maintainers
Maintainer is the person(s) you should contact with updates,
corrections and patches.
Stas Bekman E<lt>stas (at) stason.orgE<gt>
=head1 Authors
=over
=item *
Stas Bekman E<lt>stas (at) stason.orgE<gt>
=back
Only the major authors are listed above. For contributors see the
Changes file.
=cut
1.1 modperl-docs/src/docs/2.0/devel/debug/c.pod
Index: c.pod
===================================================================
=head1 NAME
Debugging mod_perl C Internals
=head1 Description
This document explains how to debug C code under mod_perl, including
mod_perl core itself.
=head1 Debug notes
META: needs more organization
=head2 Setting gdb breakpoints with mod_perl built as DSO
If mod_perl is built as a DSO module, you cannot set the breakpoint in
the mod_perl source files when the I<httpd> program gets loaded into
the debugger. The reason is simple: At this moment I<httpd> has no
idea about mod_perl module yet. After the configuration file is
processed and the mod_perl DSO module is loaded then the breakpoints
in the source of mod_perl itself can be set.
The trick is to break at I<apr_dso_load>, let it load
I<libmodperl.so>, then you can set breakpoints anywhere in the modperl
code:
% gdb httpd
(gdb) b apr_dso_load
(gdb) run -DONE_PROCESS
[New Thread 1024 (LWP 1600)]
[Switching to Thread 1024 (LWP 1600)]
Breakpoint 1, apr_dso_load (res_handle=0xbfffb48c, path=0x811adcc
"/home/stas/apache.org/modperl-perlmodule/src/modules/perl/libmodperl.so",
pool=0x80e1a3c) at dso.c:138
141 void *os_handle = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
(gdb) finish
...
Value returned is $1 = 0
(gdb) b modperl_hook_init
(gdb) continue
This example shows how to set a breakpoint at I<modperl_hook_init>.
To automate things you can put those in the I<.gdb-jump-to-init> file:
b apr_dso_load
run -DONE_PROCESS -d `pwd`/t -f `pwd`/t/conf/httpd.conf
finish
b modperl_hook_init
continue
and then start the debugger with:
% gdb /home/stas/httpd-2.0/bin/httpd -command \
`pwd`/t/.gdb-jump-to-init
=head2 Starting the Server Fast under gdb
When the server is started under gdb, it first loads the symbol tables
of the dynamic libraries that it sees going to be used. Some versions
of gdb may take ages to complete this task, which makes the debugging
very irritating if you have to restart the server all the time and it
doesn't happen immediately.
The trick is to set the C<auto-solib-add> flag to 0:
set auto-solib-add 0
as early as possible in I<~/.gdbinit> file.
With this setting in effect, you can load only the needed dynamic
libraries with I<sharedlibrary> command. Remember that in order to set
a breakpoint and step through the code inside a certain dynamic
library you have to load it first. For example consider this gdb
commands file:
.gdb-commands
------------
file ~/httpd/prefork/bin/httpd
handle SIGPIPE pass
handle SIGPIPE nostop
set auto-solib-add 0
b ap_run_pre_config
run -d `pwd`/t -f `pwd`/t/conf/httpd.conf \
-DONE_PROCESS -DAPACHE2 -DPERL_USEITHREADS
sharedlibrary modperl
b modperl_hook_init
# start: modperl_hook_init
continue
# restart: ap_run_pre_config
continue
# restart: modperl_hook_init
continue
b apr_poll
continue
# load APR/PerlIO/PerlIO.so
sharedlibrary PerlIO
b PerlIOAPR_open
which can be used as:
% gdb -command=.gdb-commands
This script stops in I<modperl_hook_init()>, so you can step through
the mod_perl startup. We had to use the I<ap_run_pre_config> so we can
load the I<libmodperl.so> library as explained earlier. Since httpd
restarts on the start, we have to I<continue> until we hit
I<modperl_hook_init> second time, where we can set the breakpoint at
I<apr_poll>, the very point where httpd polls for new request and run
again I<continue> so it'll stop at I<apr_poll>. This particular script
passes over modperl_hook_init(), since we run the C<continue> command
a few times to reach the I<apr_poll> breakpoint. See the L<Precooked
gdb Startup Scripts|/Precooked_gdb_Startup_Scripts> section for
standalone script examples.
When gdb stops at the function I<apr_poll> it's a time to start the
client, that will issue a request that will exercise the server
execution path we want to debug. For example to debug the
implementation of C<APR::Pool> we may run:
% t/TEST -run apr/pool
which will trigger the run of a handler in
I<t/response/TestAPR/pool.pm> which in turn tests the C<APR::Pool>
code.
But before that if we want to debug the server response we need to set
breakpoints in the libraries we want to debug. For example if we want
to debug the function C<PerlIOAPR_open> which resides in
I<APR/PerlIO/PerlIO.so> we first load it and then we can set a
breakpoint in it. Notice that gdb may not be able to load a library if
it wasn't referenced by any of the code. In this case we have to load
this library at the server startup. In our example we load:
PerlModule APR::PerlIO
in I<httpd.conf>. To check which libraries' symbol tables can be
loaded in gdb, run (when the server has been started):
gdb> info sharedlibrary
which also shows which libraries are loaded already.
Also notice that you don't have to type the full path of the library
when trying to load them, even a partial name will suffice. In our
commands file example we have used C<sharedlibrary modperl> instead of
saying C<sharedlibrary libmodperl.so>.
If you want to set breakpoints and step through the code in the Perl
and APR core libraries you should load their appropriate libraries:
gdb> sharedlibrary libperl
gdb> sharedlibrary libapr
gdb> sharedlibrary libaprutil
Setting I<auto-solib-add> to 0 makes the debugging process unusual,
since originally gdb was loading the dynamic libraries automatically,
whereas now it doesn't. This is the price one has to pay to get the
debugger starting the program very fast. Hopefully the future versions
of gdb will improve.
Just remember that if you try to I<step-in> and debugger doesn't do
anything, that means that the library the function is located in
wasn't loaded. The solution is to create a commands file as explained
in the beginning of this section and craft the startup script the way
you need to avoid extra typing and mistakes when repeating the same
debugging process again and again.
Another important thing is that whenever you want to be able to see
the source code for the code you are stepping through, the library or
the executable you are in must have the debug symbols present. That
means that the code has to be compiled with I<-g> option for the gcc
compiler. For example if I want to set a breakpoint in /lib/libc.so, I
can do that by loading:
gdb> sharedlibrary /lib/libc.so
But most likely that this library has the debug symbols stripped off,
so while gdb will be able to break at the breakpoint set inside this
library, you won't be able to step through the code. In order to do
so, recompile the library to add the debug symbols.
If debug code in response handler you usually start the client after
the server was started, when doing this a lot you may find it annoying
to need to wait before the client can be started. Therefore you can
use a few tricks to do it in one command. If the server starts fast
you can use sleep():
% ddd -command=.debug-modperl-init & ; \
sleep 2 ; t/TEST -verbose -run apr/pool
or the C<Apache::Test> framework's C<-ping=block> option:
% ddd -command=.debug-modperl-init & ; \
t/TEST -verbose -run -ping=block apr/pool
which will block till the server starts responding, and only then will
try to run the test.
=head2 Precooked gdb Startup Scripts
Here are a few startup scripts you can use with gdb to accomplish one
of the common debugging tasks. To execute the startup script, simply run:
% gdb -command=.debug-script-filename
They can be run under gdb and any of the gdb front-ends. For example
to run the scripts under C<ddd> substitute C<gdb> with C<ddd>:
% ddd -command=.debug-script-filename
=over
=item * Debugging mod_perl Initialization
The F<code/.debug-modperl-init> startup script breaks at the
C<modperl_hook_init()> function, which is useful for debugging code at
the modperl's initialization phase.
=item * Debugging mod_perl's Hooks Registeration With httpd
Similar to the previous startup script, the
F<code/.debug-modperl-register> startup script breaks at the
C<modperl_register_hooks()>, which is the very first hook called in
the mod_perl land. Therefore use this one if you need to start
debugging at an even earlier entry point into mod_perl.
Refer to the notes inside the script to adjust it for a specific
I<httpd.conf> file.
=item * Debugging mod_perl XS Extensions
The F<code/.debug-modperl-xs> startup script breaks at the
C<mpxs_Apache__Filter_print()> function implemented in
I<xs/Apache/Filter/Apache__Filter.h>. This is an example of debugging
code in XS Extensions. For this particular example the complete test
case is:
% ddd -command=.debug-modperl-xs & \
t/TEST -v -run -ping=block filter/api
When I<filter/api> test is running it calls
mpxs_Apache__Filter_print() which is when the breakpoint is reached.
=back
=head1 Obtaining Core Files
META: need to review (unfinished)
=head2 ...
=head1 Analyzing Dumped Core Files
When your application dies with the I<Segmentation fault> error (which
generates a C<SIGSEGV> signal) and optionally generates a I<core> file
you can use C<gdb> or a similar debugger to find out what caused the
I<Segmentation fault> (or I<segfault> as we often call it).
=head2 Getting Ready to Debug
In order to debug the I<core> file we may need to recompile Perl and
mod_perl with debugging symbols inside. Usually you have to recompile
only mod_perl, but if the I<core> dump happens in the I<libmodperl.so>
library and you want to see the whole backtrace, you probably want to
recompile Perl as well.
Recompile Perl with I<-DDEBUGGING> during the ./Configure stage (or
even better with I<-Doptimize="-g"> which in addition to adding the
C<-DDEBUGGING> option, adds the I<-g> options which allows you to
debug the Perl interpreter itself).
After recompiling Perl, recompile mod_perl with C<MP_DEBUG=1> during
the I<Makefile.PL> stage.
Building mod_perl with C<PERL_DEBUG=1> will:
=over
=item 1
add `-g' to EXTRA_CFLAGS
=item 1
turn on MP_TRACE (tracing)
=item 1
Set PERL_DESTRUCT_LEVEL=2
=item 1
Link against C<libperld> if -e
$Config{archlibexp}/CORE/libperld$Config{lib_ext}
=back
If you build a static mod_perl, remember that during I<make install>
Apache strips all the debugging symbols. To prevent this you should
use the Apache I<--without-execstrip> C<./configure> option. So if you
configure Apache via mod_perl, you should do:
panic% perl Makefile.PL USE_APACI=1 \
APACI_ARGS='--without-execstrip' [other options]
Alternatively you can copy the unstripped binary manually. For example
we did this to give us an Apache binary called C<httpd_perl> which
contains debugging symbols:
panic# cp httpd-2.x/httpd /home/httpd/httpd_perl/bin/httpd_perl
Now the software is ready for a proper debug.
=head2 Creating a Faulty Package
Next stage is to create a package that aborts abnormally with the
I<Segmentation fault> error. We will write faulty code on purpose,
so you will be able to reproduce the problem and exercise the
debugging technique explained here. Of course in a real case you
will have some real bug to debug, so in that case you may want to
skip this stage of writing a program with a deliberate bug.
We will use the C<Inline.pm> module to embed some code written in C
into our Perl script. The faulty function that we will add is this:
void segv() {
int *p;
p = NULL;
printf("%d", *p); /* cause a segfault */
}
For those of you not familiar with C programming, I<p> is a pointer to
a segment of memory. Setting it to C<NULL> ensures that we try to
read from a segment of memory to which the operating system does not
allow us access, so of course dereferencing C<NULL> pointer causes a
segmentation fault. And that's what we want.
So let's create the C<Bad::Segv> package. The name I<Segv> comes from
the C<SIGSEGV> (segmentation violation signal) that is generated when
the I<Segmentation fault> occurs.
First we create the installation sources:
panic% h2xs -n Bad::Segv -A -O -X
Writing Bad/Segv/Segv.pm
Writing Bad/Segv/Makefile.PL
Writing Bad/Segv/test.pl
Writing Bad/Segv/Changes
Writing Bad/Segv/MANIFEST
Now we modify the I<Segv.pm> file to include the C code. Afterwards
it looks like this:
package Bad::Segv;
use strict;
BEGIN {
$Bad::Segv::VERSION = '0.01';
}
use Inline C => <<'END_OF_C_CODE';
void segv() {
int *p;
p = NULL;
printf("%d", *p); /* cause a segfault */
}
END_OF_C_CODE
1;
Finally we modify I<test.pl>:
use Inline SITE_INSTALL;
BEGIN { $| = 1; print "1..1\n"; }
END {print "not ok 1\n" unless $loaded;}
use Bad::Segv;
$loaded = 1;
print "ok 1\n";
Note that we don't test Bad::Segv::segv() in I<test.pl>, since this
will abort the I<make test> stage abnormally, and we don't want this.
Now we build and install the package:
panic% perl Makefile.PL
panic% make && make test
panic% su
panic# make install
Running I<make test> is essential for C<Inline.pm> to prepare the binary
object for the installation during I<make install>.
META: stopped here!
Now we can test the package:
panic% ulimit -c unlimited
panic% perl -MBad::Segv -e 'Bad::Segv::segv()'
Segmentation fault (core dumped)
panic% ls -l core
-rw------- 1 stas stas 1359872 Feb 6 14:08 core
Indeed, we can see that the I<core> file was dumped, which will be used
to present the debug techniques.
=head2 Getting the core File Dumped
Now let's get the I<core> file dumped from within the mod_perl
server. Sometimes the program aborts abnormally via the SIGSEGV signal
(I<Segmentation Fault>), but no I<core> file is dumped. And without
the I<core> file it's hard to find the cause of the problem, unless
you run the program inside C<gdb> or another debugger in first
place. In order to get the I<core> file, the application has to:
=over
=item *
have the effective UID the same as real UID (the same goes for
GID). Which is the case of mod_perl unless you modify these settings
in the program.
=item *
be running from a directory which at the moment of the
I<Segmentation fault> is writable by the process. Notice that the
program might change its current directory during its run, so it's
possible that the I<core> file will need to be dumped in a different
directory from the one the program was started from. For example when
mod_perl runs an C<Apache::Registry> script it changes its directory
to the one in which the script source is located.
=item *
be started from a shell process with sufficient resource allocations
for the I<core> file to be dumped. You can override the default
setting from within a shell script if the process is not started
manually. In addition you can use C<BSD::Resource> to manipulate the
setting from within the code as well.
You can use C<ulimit> for C<bash> and C<limit> for C<csh> to check and
adjust the resource allocation. For example inside C<bash>, you may
set the core file size to unlimited:
panic% ulimit -c unlimited
or for C<csh>:
panic% limit coredumpsize unlimited
For example you can set an upper limit on the I<core> file size to 8MB
with:
panic% ulimit -c 8388608
So if the core file is bigger than 8MB it will be not created.
=item *
Of course you have to make sure that you have enough disk space to
create a big core file (mod_perl I<core> files tend to be of a few
MB in size).
=back
Note that when you are running the program under a debugger like
C<gdb>, which traps the C<SIGSEGV> signal, the I<core> file will not
be dumped. Instead it allows you to examine the program stack and
other things without having the I<core> file.
So let's write a simple script that uses C<Bad::Segv>:
core_dump.pl
------------
use strict;
use Bad::Segv ();
use Cwd()
my $r = shift;
$r->content_type('text/plain');
my $dir = getcwd;
$r->print("The core should be found at $dir/core\n");
Bad::Segv::segv();
In this script we load the C<Bad::Segv> and C<Cwd> modules. After
that we acquire the request object and send the HTTP header. Now we
come to the real part--we get the current working directory, print out
the location of the I<core> file that we are about to dump and finally
we call Bad::Segv::segv() which dumps the I<core> file.
Before we run the script we make sure that the shell sets the I<core>
file size to be unlimited, start the server in single server mode as a
non-root user and generate a request to the script:
panic% cd /home/httpd/httpd_perl/bin
panic% limit coredumpsize unlimited
panic% ./httpd_perl -X
# issue a request here
Segmentation fault (core dumped)
Our browser prints out:
The core should be found at /home/httpd/perl/core
And indeed the core file appears where we were told it will (remember
that C<Apache::Registry> scripts change their directory to the
location of the script source):
panic% ls -l /home/httpd/perl/core
-rw------- 1 stas httpd 3227648 Feb 7 18:53 /home/httpd/perl/core
As you can see it's a 3MB I<core> file. Notice that mod_perl was
started as user I<stas>, which had write permission for directory
I</home/httpd/perl>.
=head2 Analyzing the core File
First we start C<gdb>:
panic% gdb /home/httpd/httpd_perl/bin/httpd_perl /home/httpd/perl/core
with the location of the mod_perl executable and the core file as the
arguments.
To see the backtrace you run the I<where> or the I<bt> command:
(gdb) where
#0 0x4025ea08 in XS_Apache__Segv_segv ()
from
/usr/lib/perl5/site_perl/5.6.0/i386-linux/auto/Bad/Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39/Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39.so
#1 0x40136528 in PL_curcopdb ()
from /usr/lib/perl5/5.6.0/i386-linux/CORE/libperl.so
Well, you can see the last commands, but our perl and mod_perl are
probably without the debug symbols. So we recompile Perl and mod_perl
with debug symbols as explained earlier in this chapter.
Now when we repeat the process of starting the server, issuing a
request and getting the core file, after which we run C<gdb> again
against the executable and the dumped I<core> file.
panic% gdb /home/httpd/httpd_perl/bin/httpd_perl /home/httpd/perl/core
Now we can see the whole backtrace:
(gdb) bt
#0 0x40323a30 in segv () at
Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39.xs:9
#1 0x40323af8 in XS_Apache__Segv_segv (cv=0x85f2b28)
at Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39.xs:24
#2 0x400fcbda in Perl_pp_entersub () at pp_hot.c:2615
#3 0x400f2c56 in Perl_runops_debug () at run.c:53
#4 0x4008b088 in S_call_body (myop=0xbffff788, is_eval=0) at perl.c:1796
#5 0x4008ac4f in perl_call_sv (sv=0x82fc2e4, flags=4) at perl.c:1714
#6 0x807350e in perl_call_handler ()
#7 0x80729cd in perl_run_stacked_handlers ()
#8 0x80701b4 in perl_handler ()
#9 0x809f409 in ap_invoke_handler ()
#10 0x80b3e8f in ap_some_auth_required ()
#11 0x80b3efa in ap_process_request ()
#12 0x80aae60 in ap_child_terminate ()
#13 0x80ab021 in ap_child_terminate ()
#14 0x80ab19c in ap_child_terminate ()
#15 0x80ab80c in ap_child_terminate ()
#16 0x80ac03c in main ()
#17 0x401b8cbe in __libc_start_main () from /lib/libc.so.6
Reading the trace from bottom to top, we can see that it starts with Apache
calls, followed by Perl syscalls. At the top we can see the segv()
call which was the one that caused the Segmentation fault, we can also
see that the faulty code was at line 9 of I<Segv.xs> file (with MD5
signature of the code in the name of the file, because of the way
C<Inline.pm> works). It's a little bit tricky with C<Inline.pm> since
we have never created any I<.xs> files ourselves, (C<Inline.pm> does
it behind the scenes). The solution in this case is to tell
C<Inline.pm> not to cleanup the build directory, so we can see the
created I<.xs> file.
We go back to the directory with the source of C<Bad::Segv> and
force the recompilation, while telling C<Inline.pm> not to cleanup
after the build and to print a lot of other useful info:
panic# cd Bad/Segv
panic# perl -MInline=FORCE,NOCLEAN,INFO Segv.pm
Information about the processing of your Inline C code:
Your module is already compiled. It is located at:
/home/httpd/perl/Bad/Segv/_Inline/lib/auto/Bad/Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39/Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39.so
But the FORCE_BUILD option is set, so your code will be recompiled.
I'll use this build directory:
/home/httpd/perl/Bad/Segv/_Inline/build/Bad/Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39/
and I'll install the executable as:
/home/httpd/perl/Bad/Segv/_Inline/lib/auto/Bad/Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39/Segv_C_0_01_e6b5959d800f515de36a7e7eeab28b39.so
The following Inline C function(s) have been successfully bound to Perl:
void segv()
It tells us that the code was already compiled, but since we have
forced it to recompile we can look at the files after the build. So we
go into the build directory reported by C<Inline.pm> and find the
I<.xs> file there, where on line 9 we indeed find the faulty code:
9: printf("%d",*p); // cause a segfault
Notice that in our example we knew what script has caused the
Segmentation fault. In a real world the chances are that you will find
the I<core> file without any clue to which of handler or script has
triggered it. The special I<curinfo> C<gdb> macro comes to help:
panic% gdb /home/httpd/httpd_perl/bin/httpd_perl /home/httpd/perl/core
(gdb) source mod_perl-x.xx/.gdbinit
(gdb) curinfo
9:/home/httpd/perl/core_dump.pl
We start the C<gdb> debugger as before. I<.gdbinit>, the file with
various useful C<gdb> macros is located in the source tree of
mod_perl. We use the C<gdb> source() function to load these macros,
and when we run the I<curinfo> macro we learn that the core was dumped
when I</home/httpd/perl/core_dump.pl> was executing the code at line
9.
These are the bits of information that are important in order to
reproduce and resolve a problem: the filename and line where the
faulty function was called (the faulty function is Bad::Segv::segv()
in our case) and the actual line where the Segementation fault occured
(the printf("%d",*p) call in XS code). The former is important for
problem reproducing, it's possible that if the same function was
called from a different script the problem won't show up (not the case
in our example, where the using of a value dereferenced from the NULL
pointer will always cause the Segmentation fault).
=head2 Obtaining core Files under Solaris
On Solaris the following method can be used to generate a core file.
=over
=item 1
Use truss(1) as I<root> to stop a process on a segfault:
panic% truss -f -l -t \!all -s \!SIGALRM -S SIGSEGV -p <pid>
or, to monitor all httpd processes (from bash):
panic% for pid in `ps -eaf -o pid,comm | fgrep httpd | cut -d'/' -f1`;
do truss -f -l -t \!all -s \!SIGALRM -S SIGSEGV -p $pid 2>&1 &
done
The used truss(1) options are:
=over
=item *
C<-f> - follow forks.
=item *
C<-l> - (that's an el) includes the thread-id and the pid (the pid is
what we want).
=item *
C<-t> - specifies the syscalls to trace,
=item *
!all - turns off the tracing of syscalls specified by C<-t>
=item *
C<-s> - specifies signals to trace and the C<!SIGALRM> turns off the
numerous alarms Apache creates.
=item *
C<-S> - specifies signals that stop the process.
=item *
C<-p> - is used to specify the pid.
=back
Instead of attaching to the process, you can start it under truss(1):
panic% truss -f -l -t \!all -s \!SIGALRM -S SIGSEGV \
/usr/local/bin/httpd -f httpd.conf 2>&1 &
=item 1
Watch the I<error_log> file for reaped processes, as when they get
SISSEGV signals. When the process is reaped it's stopped but not
killed.
=item 1
Use gcore(1) to get a I<core> of stopped process or attach to it with
gdb(1). For example if the process id is 662:
%panic gcore 662
gcore: core.662 dumped
Now you can load this I<core> file in gdb(1).
=item 1
C<kill -9> the stopped process. Kill the truss(1) processes as well,
if you don't need to trap other segfaults.
=back
Obviously, this isn't great to be doing on a production system since
truss(1) stops the process after it dumps core and prevents Apache
from reaping it. So, you could hit the clients/threads limit if you
segfault a lot.
=head1 Expanding C Macros
Perl, mod_perl and httpd C code makes an extensive use of C macros,
which sometimes use many other macros in their definitions, so it
becomes quite a task to figure out how to figure out what a certain
macro expands to, especially when the macro expands to different
values in differnt environments. Luckily there are ways to automate
the expansion process.
=head2 Expanding C Macros with C<make>
The mod_perl I<Makefile>'s include a rule for macro expansions which
you can find by looking for the C<c.i.> rule. To expand all macros in
a certain C file, you should run C<make filename.c>, which will create
I<filename.i> with all macros expanded in it. For example to create
I<apr_perlio.i> with all macros used in I<apr_perlio.c>:
% cd modperl-2.0/xs/APR/PerlIO
% make apr_perlio.i
the I<apr_perlio.i> file now lists all the macros:
% less apr_perlio.i
# 1 "apr_perlio.c"
# 1 "<built-in>"
#define __VERSION__ "3.1.1 (Mandrake Linux 8.3 3.1.1-0.4mdk)"
...
=head2 Expanding C Macros with C<gdb>
With gcc-3.1 or higher and gdb-5.2-dev or higher you can expand macros
in gdb, when you step through the code. e.g.:
(gdb) macro expand pTHX_
expands to: PerlInterpreter *my_perl __attribute__((unused)),
(gdb) macro expand PL_dirty
expands to: (*Perl_Tdirty_ptr(my_perl))
For each library that you want to use this feature with you have to
compile it with:
CFLAGS="-gdwarf-2 -g3"
or whatever is appropriate for your system, refer to the gcc manpage
for more info.
To compile perl with this debug feature, pass C<-Doptimize='-gdwarf-2
-g3'> to C<./Configure>. For Apache run:
CFLAGS="-gdwarf-2 -g3" ./configure [...]
for mod_perl you don't have to do anything, as it'll pick the
C<$Config{optimize}> Perl flags automatically, if Perl is compiled
with C<-DDEBUGGING> (which is implied on most systems, if you use
C<-Doptimize='-g'> or similar.)
Notice that this will make your libraries B<huge>! e.g. on Linux 2.4
Perl 5.8.0's normal I<libperl.so> is about 0.8MB on linux, compiled
with C<-Doptimize='-g'> about 2.7MB and with C<-Doptimize='-gdwarf-2
-g3'> 12.5MB. C<httpd> is also becomes about 10 times bigger with this
feature enabled. I<mod_perl.so> instead of 0.2k becomes 11MB. You get
the idea. Of course since you may want this only during the
development/debugging, that shouldn't be a problem.
The complete details are at:
http://sources.redhat.com/gdb/current/onlinedocs/gdb_10.html#SEC69
=head1 Maintainers
Maintainer is the person(s) you should contact with updates,
corrections and patches.
Stas Bekman E<lt>stas (at) stason.orgE<gt>
=head1 Authors
=over
=item *
Stas Bekman E<lt>stas (at) stason.orgE<gt>
=back
Only the major authors are listed above. For contributors see the
Changes file.
=cut
1.1 modperl-docs/src/docs/2.0/devel/debug/perl.pod
Index: perl.pod
===================================================================
=head1 NAME
Debugging mod_perl Perl Internals
=head1 Description
This document explains how to debug Perl code under mod_perl.
=head1 Maintainers
Maintainer is the person(s) you should contact with updates,
corrections and patches.
=over
=item *
Stas Bekman E<lt>stas (at) stason.orgE<gt>
=back
=head1 Authors
=over
=item *
Stas Bekman E<lt>stas (at) stason.orgE<gt>
=back
Only the major authors are listed above. For contributors see the
Changes file.
=cut
1.1
modperl-docs/src/docs/2.0/devel/debug/code/.debug-modperl-init
Index: .debug-modperl-init
===================================================================
# This gdb startup script breaks at the modperl_hook_init() function,
# which is useful for debug things at the modperl init phase.
#
# Invoke as:
# gdb -command=.debug-modperl-init
#
# see ADJUST notes for things that may need to be adjusted
# ADJUST: the path to the httpd executable if needed
file ~/httpd/worker/bin/httpd
handle SIGPIPE nostop
handle SIGPIPE pass
set auto-solib-add 0
define myrun
tbreak main
break ap_run_pre_config
# ADJUST: the httpd.conf file's path if needed
# ADJUST: add -DPERL_USEITHREADS to debug threaded mpms
run -d `pwd`/t -f `pwd`/t/conf/httpd.conf -DONE_PROCESS -DAPACHE2
continue
end
define modperl_init
sharedlibrary modperl
b modperl_hook_init
continue
end
define sharedap
sharedlibrary apr
sharedlibrary aprutil
#sharedlibrary mod_ssl.so
continue
end
define sharedperl
sharedlibrary libperl
end
# start the server and run till modperl_hook_init on start
myrun
modperl_init
# ADJUST: uncomment to reach modperl_hook_init on restart
#continue
#continue
# ADJUST: uncomment if you need to step through the code in apr libs
#sharedap
# ADJUST: uncomment if you need to step through the code in perlib
#sharedperl
1.1
modperl-docs/src/docs/2.0/devel/debug/code/.debug-modperl-register
Index: .debug-modperl-register
===================================================================
# This gdb startup script allows to break at the very first invocation
# of mod_perl initialization, just after it was loaded. When the
# perl_module is loaded, and its pointer struct is added via
# ap_add_module(), the first hook that will be called is
# modperl_register_hooks().
#
# Invoke as:
# gdb -command=.debug-modperl-register
#
# see ADJUST notes for things that may need to be adjusted
define sharedap
sharedlibrary apr
sharedlibrary aprutil
#sharedlibrary mod_ssl.so
end
define sharedperl
sharedlibrary libperl
end
### Run ###
# ADJUST: the path to the httpd executable if needed
file ~/httpd/prefork/bin/httpd
handle SIGPIPE nostop
handle SIGPIPE pass
set auto-solib-add 0
tbreak main
# assuming that mod_dso is compiled in
b load_module
# ADJUST: the httpd.conf file's path if needed
# ADJUST: add -DPERL_USEITHREADS to debug threaded mpms
run -d `pwd`/t -f `pwd`/t/conf/httpd.conf \
-DONE_PROCESS -DNO_DETATCH -DAPACHE2
# skip over 'tbreak main'
continue
# In order to set the breakpoint in mod_perl.so, we need to get to
# the point where it's loaded.
#
# With static mod_perl, the bp can be set right away
#
# With DSO mod_perl, mod_dso's load_module() loads the mod_perl.so
# object and it immediately calls ap_add_module(), which calls
# modperl_register_hooks(). So if we want to bp at the latter, we need
# to stop at load_module(), set the 'bp modperl_register_hooks' and
# then continue.
# Assuming that 'LoadModule perl_module' is the first LoadModule
# directive in httpd.conf, you need just one 'continue' after
# 'ap_add_module'. If it's not the first one, you need to add as many
# 'continue' commands as the number of 'LoadModule foo' before
# perl_module, but before setting the 'ap_add_module' bp.
#
# If mod_perl is compiled statically, everything is already preloaded,
# so you can set modperl_* the breakpoints right away
b ap_add_module
continue
sharedlibrary modperl
b modperl_register_hooks
continue
#b modperl_hook_init
#b modperl_config_srv_create
#b modperl_startup
#b modperl_init_vhost
#b modperl_dir_config
#b modperl_cmd_load_module
#modperl_config_apply_PerlModule
# ADJUST: uncomment if you need to step through the code in apr libs
#sharedap
# ADJUST: uncomment if you need to step through the code in perlib
#sharedperl
1.1
modperl-docs/src/docs/2.0/devel/debug/code/.debug-modperl-xs
Index: .debug-modperl-xs
===================================================================
# This gdb startup script breaks at the mpxs_Apache__Filter_print()
# function from the XS code, as an example how you can debug the code
# in XS extensions.
#
# Invoke as:
# gdb -command=.debug-modperl-xs
# and then run:
# t/TEST -v -run -ping=block filter/api
#
# see ADJUST notes for things that may need to be adjusted
# ADJUST: the path to the httpd executable if needed
file /home/stas/httpd/worker/bin/httpd
handle SIGPIPE nostop
handle SIGPIPE pass
set auto-solib-add 0
define myrun
tbreak main
break ap_run_pre_config
# ADJUST: the httpd.conf file's path if needed
# ADJUST: add -DPERL_USEITHREADS to debug threaded mpms
run -d `pwd`/t -f `pwd`/t/conf/httpd.conf \
-DONE_PROCESS -DNO_DETATCH -DAPACHE2
continue
end
define sharedap
sharedlibrary apr
sharedlibrary aprutil
#sharedlibrary mod_ssl.so
continue
end
define sharedperl
sharedlibrary libperl
end
define gopoll
b apr_poll
continue
continue
end
define mybp
# load Apache/Filter.so
sharedlibrary Filter
b mpxs_Apache__Filter_print
# no longer needed and they just make debugging harder under threads
disable 2
disable 3
continue
end
myrun
gopoll
mybp
# ADJUST: uncomment if you need to step through the code in apr libs
#sharedap
# ADJUST: uncomment if you need to step through the code in perlib
#sharedperl
1.1
modperl-docs/src/docs/2.0/devel/performance/size_matters.pod
Index: size_matters.pod
===================================================================
=head1 NAME
Measure sizeof() of Perl's C Structures
=head1 Description
This document describes the I<sizeof> various structures, as
determined by I<util/sizeof.pl>. These measurements are mainly for
research purposes into making Perl things smaller, or rather, how to
use less Perl things.
=head1 Perl Structures
Structures diagrams are courtesy gdb (print pretty) and a bit of hand
crafting.
=over 4
=item CV - 229 minimum, 254 minimum w/ symbol table entry
cv = {
sv_any = { // XPVCV *
xpv_pv = 0x0, // char *
xpv_cur = 0, // STRLEN
xpv_len = 0, // STRLEN
xof_off = 0 , // IV
xnv_nv = 0, // NV
xmg_magic = 0x0, // MAGIC *
xmg_stash = 0x0, // HV *
xcv_stash = 0x0, // HV *
xcv_start = 0x0, // OP *
xcv_root = 0x0, // OP *
xcv_xsub = 0x0, // void (*)(register PerlInterpreter *, CV *)
xcv_xsubany = { // ANY
any_ptr = 0x0,
any_i32 = 0,
any_iv = 0,
any_long = 0,
any_dptr = 0,
any_dxptr = 0
},
xcv_gv = { // GV *
sv_any = { // void *
xpv_pv = 0x0, // char *
xpv_cur = 0, // STRLEN
xpv_len = 0, // STRLEN
xiv_iv = 0, // IV
xnv_nv = 0, // NV
xmg_magic = { // MAGIC *
mg_moremagic = 0x0, // MAGIC *
mg_virtual = 0x0, // MGVTBL *
mg_private = 0, // U16
mg_type = 0, // char
mg_flags = 0, // U8
mg_obj = 0x0, // SV *
mg_ptr = 0x0, // char *
mg_len = 0, // I32
},
xmg_stash = 0x0, // HV *
xgv_gp = { // GP *
gp_sv = { // SV *
sv_any = 0x0, // void *
sv_refcnt = 0, // U32
sv_flags = 0 // U32
},
gp_refcnt = 0, // U32
gp_io = 0x0, // struct io *
gp_form = 0x0, // CV *
gp_av = 0x0, // AV *
gp_hv = 0x0, // HV *
gp_egv = 0x0, // GV *
gp_cv = 0x0, // CV *
gp_cvgen = 0, // U32
gp_flags = 0, // U32
gp_line = 0, // line_t
gp_file = 0x0, // char *
},
xgv_name = 0x0, // char *
xgv_namelen = 0, // STRLEN
xgv_stash = 0x0, // void *
xgv_flags = 0, // U8
},
sv_refcnt = 0, // U32
sv_flags = 0, // U32
},
xcv_file = 0x0, // char *
xcv_depth = 0, // long
xcv_padlist = 0x0, // AV *
xcv_outside = 0x0, // CV *
xcv_flags = 0, // cv_flags_t
}
sv_refcnt = 0, // U32
sv_flags = 0, // U32
};
In addition to the minimum bytes:
=over 4
=item name of the subroutine: GvNAMELEN(CvGV(cv))+1
=item symbol table entry: HvENTRY (25 + GvNAMELEN(CvGV(cv))+1)
=item minimum sizeof(AV) * 3: xcv_padlist if !CvXSUB(cv)
=item CvROOT(cv) optree
=back
=item HV - 60 minmum
hv = {
sv_any = { // SV *
xhv_array = 0x0, // char *
xhv_fill = 0, // STRLEN
xhv_max = 0, // STRLEN
xhv_keys = 0, // IV
xnv_nv = 0, // NV
xmg_magic = 0x0, // MAGIC *
xmg_stash = 0x0, // HV *
xhv_riter = 0, // I32
xhv_eiter = 0x0, // HE *
xhv_pmroot = 0x0, // PMOP *
xhv_name = 0x0 // char *
},
sv_refcnt = 0, // U32
sv_flags = 0, // U32
};
Each entry adds C<sizeof(HvENTRY)>, minimum of 7 (initial C<xhv_max>).
Note that keys of the same value share C<sizeof(HEK)>, across all
hashes.
=item HvENTRY - 25 + HeKLEN+1
sizeof(HE *) + sizeof(HE) + sizeof(HEK)
=item HE - 12
he = {
hent_next = 0x0, // HE *
hent_hek = 0x0, // HEK *
hent_val = 0x0 // SV *
};
=item HEK - 9 + hek_len
hek = {
hek_hash = 0, // U32
hek_len = 0, // I32
hek_key = 0, // char
};
=item AV - 53
av = {
sv_any = { // SV *
xav_array = 0x0, // char *
xav_fill = 0, // size_t
xav_max = 0, // size_t
xof_off = 0, // IV
xnv_nv = 0, // NV
xmg_magic = 0x0, // MAGIC *
xmg_stash = 0x0, // HV *
xav_alloc = 0x0, // SV **
xav_arylen = 0x0, // SV *
xav_flags = 0, // U8
},
sv_refcnt = 0, // U32
sv_flags = 0 // U32
};
In addition to the minimum bytes:
=over 4
=item AvFILL(av) * sizeof(SV *)
=back
=back
=head1 SEE ALSO
perlguts(3), B::Size(3),
http://gisle.aas.no/perl/illguts/
=head1 Maintainers
Maintainer is the person(s) you should contact with updates,
corrections and patches.
=over
=item *
Doug MacEachern E<lt>dougm (at) covalent.netE<gt>
=back
=head1 Authors
=over
=item *
Doug MacEachern E<lt>dougm (at) covalent.netE<gt>
=back
=cut
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]