Profiling Apache modules with DTrace


While poking around the Apache source code this weekend, I started to wonder which Apache modules consumed the most CPU time servicing HTTP requests. Since I had Apache installed on a Solaris 10 box, I threw together a D script named apachemoduleprof to answer this question:

$ apachemoduleprof pgrep httpd


Started Apache Request Profiler (Control-C to stop...)

New connection 

Connection start: 2005 Dec 12 18:38:03
Connection stop : 2005 Dec 12 18:38:04

Method URI Count
GET / 1

Module Processing Time
mod_actions.so 24005 ns
mod_expires.so 47337 ns
mod_log_forensic.so 61466 ns
mod_dir.so 74525 ns
mod_info.so 76032 ns
mod_autoindex.so 76927 ns
mod_userdir.so 80814 ns
mod_speling.so 83850 ns
mod_vhost_alias.so 84157 ns
mod_asis.so 94340 ns
mod_cgi.so 99906 ns
mod_usertrack.so 100358 ns
mod_imap.so 101102 ns
mod_cern_meta.so 112784 ns
mod_headers.so 143674 ns
mod_status.so 147744 ns
mod_access.so 169724 ns
mod_dav.so 175717 ns
mod_env.so 177622 ns
mod_unique_id.so 247845 ns
mod_negotiation.so 267640 ns
mod_auth_digest.so 272762 ns
mod_dtrace.so 372322 ns
mod_rewrite.so 437157 ns
mod_include.so 905436 ns
mod_alias.so 1717912 ns
mod_log_config.so 1982180 ns
mod_setenvif.so 2803176 ns
mod_mime.so 3748429 ns
httpd 17620455 ns
Total 32 ms

Hook Name Module Hook Function Time
ap_run_log_transaction mod_log_forensic.so log_after 20739 ns
ap_run_handler mod_actions.so action_handler 24005 ns
ap_run_pre_connection mod_dtrace.so apache_pre_connection 24587 ns
ap_run_access_checker httpd do_nothing 32272 ns
ap_run_header_parser mod_setenvif.so match_headers 40581 ns
ap_run_post_read_request mod_log_forensic.so log_before 40727 ns
ap_run_insert_filter httpd core_insert_filter 43937 ns
ap_run_post_read_request mod_dtrace.so apache_receive_request 45105 ns
ap_run_access_checker mod_dtrace.so apache_check_access 45678 ns
ap_run_insert_filter mod_expires.so expires_insert_filter 47337 ns
ap_run_handler mod_negotiation.so handle_map_file 48780 ns
ap_run_create_connection mod_dtrace.so apache_create_connection 53110 ns
ap_run_insert_filter mod_headers.so ap_headers_insert_output_filter 54983 ns
ap_run_handler mod_dav.so dav_handler 70031 ns
ap_run_handler mod_rewrite.so handler_redirect 71183 ns
ap_run_log_transaction mod_dtrace.so apache_log_request 71983 ns
ap_run_fixups mod_dir.so fixup_dir 74525 ns
ap_run_handler mod_info.so display_info 76032 ns
ap_run_handler mod_autoindex.so handle_autoindex 76927 ns
ap_run_translate_name mod_userdir.so translate_userdir 80814 ns
ap_run_fixups mod_speling.so check_speling 83850 ns
ap_run_translate_name mod_vhost_alias.so mva_translate 84157 ns
ap_run_translate_name mod_rewrite.so hook_uri2file 85999 ns
ap_run_fixups mod_headers.so ap_headers_fixup 88691 ns
ap_run_handler mod_asis.so asis_handler 94340 ns
ap_run_fixups mod_rewrite.so hook_fixup 96737 ns
ap_run_fixups mod_negotiation.so fix_encoding 98537 ns
ap_run_handler mod_cgi.so cgi_handler 99906 ns
ap_run_fixups mod_usertrack.so spot_cookie 100358 ns
ap_run_fixups mod_auth_digest.so add_auth_info 100773 ns
ap_run_handler mod_imap.so imap_handler 101102 ns
ap_run_fixups httpd core_override_type 105600 ns
ap_run_fixups mod_dav.so dav_fixups 105686 ns
ap_run_map_to_storage httpd ap_send_http_trace 108333 ns
ap_run_fixups mod_cern_meta.so add_cern_meta_data 112784 ns
ap_run_type_checker mod_negotiation.so handle_multi 120323 ns
ap_run_create_request mod_dtrace.so apache_create_request 131859 ns
ap_run_handler mod_status.so status_handler 147744 ns
ap_run_pre_connection httpd core_pre_connection 160052 ns
ap_run_access_checker mod_access.so check_dir_access 169724 ns
ap_run_fixups mod_alias.so fixup_redir 170886 ns
ap_run_post_read_request mod_auth_digest.so parse_hdr_and_update_nc 171989 ns
ap_run_fixups mod_env.so fixup_env_module 177622 ns
ap_run_type_checker mod_rewrite.so hook_mimetype 183238 ns
ap_run_post_read_request mod_unique_id.so gen_unique_id 247845 ns
ap_run_create_request httpd http_create_request 389379 ns
ap_run_translate_name httpd ap_core_translate 444739 ns
ap_run_create_request httpd core_create_req 624600 ns
ap_run_fixups mod_include.so include_fixup 905436 ns
ap_run_create_connection httpd core_create_conn 1042453 ns
ap_run_translate_name mod_alias.so translate_alias_redir 1547026 ns
ap_run_log_transaction mod_log_config.so multi_log_transaction 1982180 ns
ap_run_post_read_request mod_setenvif.so match_headers 2762595 ns
ap_run_type_checker mod_mime.so find_ct 3748429 ns
ap_run_handler httpd default_handler 6766633 ns
ap_run_map_to_storage httpd core_map_to_storage 7902457 ns

After running this a few times, I noticed that the httpd core along with a few other modules consumed most of the CPU time. To see which functions were the busiest in each module, I threw together the apachemodulefunctime D script:

$ apachemodulefunctime pgrep httpd mod_log_config.so

Tracing Apache Module mod_log_config.so (Control-C to stop)
Module Function Time
mod_log_config.so log_remote_user 21732
mod_log_config.so log_request_line 22737
mod_log_config.so clf_log_bytes_sent 24603
mod_log_config.so log_remote_logname 51210
mod_log_config.so log_remote_host 52674
mod_log_config.so constant_item 118539
mod_log_config.so log_status 140340
mod_log_config.so ap_default_log_writer 444106
mod_log_config.so config_log_transaction 523044
mod_log_config.so multi_log_transaction 597611
mod_log_config.so log_request_time 896752

Total Time: 2893348

This example shows each function that is called in the mod_log_config Apache module, and provides a timestamp from the function entry to the function exit. This was super useful information, but I also wanted to see which functions were called from mod_log_config. This was easy enough with the following DTrace script:

$ cat whocall.d

dtrace:::BEGIN
{
once = 1;
}

pid$target::log_request_time:entry
{
self->follow = 1;
}

pid$target::log_request_time:return
{
self->follow = 0;
once = 0;
}

pid$target:::entry
/self->follow && once/
{
self->ts = timestamp;
}

pid$target:::return
/self->follow && once/
{
@functions[probefunc] = count();
@total[probefunc] = sum(timestamp - self->ts);
}

dtrace:::END
{
printf("%-30s %-20sn","Function", "Times Called");
printa("%-30s %-@20dn",@functions);

printf("n%-30s %-20sn","Function", "Total Execution Time");
printa("%-30s %-@20dn",@total);
}

$ dtrace -p pgrep httpd -q -s whocall.d

Function Times Called
.rem 1
ap_explode_recent_localtime 1
apr_palloc 1
apr_snprintf 1
apr_time_exp_lt 1
apr_vformatter 1
cached_explode 1
explode_time 1
findenv 1
getenv 1
getsystemTZ 1
lmutex_lock 1
lmutex_unlock 1
localtime_r 1
ltzset_u 1
offtime_u 1
set_zone_context 1
strlen 1
.div 2
__moddi3 2
strcmp 2
__divdi3 3
conv_10 7
.udiv 14

Function Total Execution Time
.rem 28896
strlen 30713
lmutex_lock 32096
lmutex_unlock 32096
offtime_u 34203
findenv 35492
ap_explode_recent_localtime 36634
set_zone_context 40452
.div 52620
strcmp 59259
__moddi3 61988
ltzset_u 84287
getsystemTZ 86064
cached_explode 88502
getenv 90303
localtime_r 91428
__divdi3 98886
apr_vformatter 140929
explode_time 147786
apr_snprintf 196226
apr_time_exp_lt 204179
.udiv 399397
conv_10 582217
apr_palloc 2114263

AS we can see from this output, the apr_palloc routine takes the longest time to execute. This is as expected, since apr_palloc() needs to call the Apache allocator to find free memory and allocate memory if allocation slots aren’t availablet. Now to see if there are any efficiency gains to be made in the allocator! :)

This article was posted by Matty on 2005-12-12 22:44:00 -0400 -0400