├── .gitignore ├── tapset ├── nginx │ ├── string.sxx │ ├── config.sxx │ ├── lua.sxx │ ├── chain.sxx │ ├── pool.sxx │ ├── upstream.sxx │ └── request.sxx ├── kernel │ └── socket.sxx ├── luajit.sxx └── luajit_gc64.sxx ├── samples ├── probe-rate.sxx ├── ctx-switches.sxx ├── lj-trace-exit-rate.sxx ├── ngx-rps.sxx ├── lj-bucket-depth-accessed.sxx ├── epoll-et-lt.sxx ├── cpu-hogs.sxx ├── epoll-loop-blocking.sxx ├── ngx-open-file-cache-misses.sxx ├── ngx-lua-count-timers.sxx ├── ngx-lj-gc-speed.sxx ├── unix-stream-socks.sxx ├── ngx-upstream-err-log.sxx ├── slow-vfs-reads.sxx ├── lj-lua-bt.sxx ├── lj-find-str.sxx ├── lj-gc.sxx ├── ngx-lua-useless-cosockets.sxx ├── ngx-upstream-next.sxx ├── epoll-loop-blocking-distr.sxx ├── luajit21-gc64 │ ├── lj-gc.sxx │ ├── lj-vm-states.sxx │ └── lj-gc-objs.sxx ├── ngx-req-pool-size.sxx ├── cpu-robbers.sxx ├── ngx-sr-trunc.sxx ├── sample-bt.sxx ├── func-latency-distr.sxx ├── zlib-deflate-chunk-size.sxx ├── ngx-count-conns.sxx ├── ngx-upstream-post-conn.sxx ├── ngx-slow-purge-reqs.sxx ├── ngx-lua-udp-recv-time.sxx ├── openssl-handshake-diagnosis.sxx ├── ngx-orig-resp-body-len.sxx ├── epoll-loop-blocking-vfs.sxx ├── ngx-lua-tcp-recv-time.sxx ├── ngx-lua-shdict-writes.sxx ├── ngx-upstream-latency.sxx ├── ngx-zlib-total-deflate-time.sxx ├── ngx-lua-udp-total-recv-time.sxx ├── ngx-pcre-dist.sxx ├── ngx-lj-trace-exits.sxx ├── ngx-zlib-deflate-time.sxx ├── lj-lua-stacks.sxx ├── sample-bt-leaks-wrapalloc.sxx ├── ngx-lua-slow-udp-query.sxx ├── ngx-lua-tcp-total-recv-time.sxx ├── ngx-req-latency-distr.sxx ├── vfs-page-cache-misses.sxx ├── ngx-req-pool-allocs.sxx ├── ngx-lua-exec-time.sxx ├── ngx-rewrite-latency-distr.sxx ├── sample-bt-leaks.sxx ├── ngx-timeout-settings.sxx ├── lj-str-tab.sxx ├── lj-vm-states.sxx ├── ngx-conn-timers.sxx ├── ngx-lua-shdict-info.sxx ├── lj-gc-objs.sxx ├── ngx-pcre-top.sxx └── ngx-single-req-latency.sxx └── stap++ /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.swo 4 | go 5 | *.o 6 | -------------------------------------------------------------------------------- /tapset/nginx/string.sxx: -------------------------------------------------------------------------------- 1 | // module nginx.string 2 | 3 | function ngx_str(s) 4 | { 5 | $*s := @cast(s, "ngx_str_t", "$^exec_path") 6 | return user_string_n($*s->data, $*s->len) 7 | } 8 | -------------------------------------------------------------------------------- /samples/probe-rate.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Copyright (C) Yichun Zhang (agentzh) 4 | 5 | global c = 0 6 | 7 | probe $^arg_probe { 8 | if ($^pid_ok) { 9 | c++ 10 | } 11 | } 12 | 13 | probe timer.s(1) { 14 | printf("%d count/sec\n", c); 15 | c = 0; 16 | } 17 | -------------------------------------------------------------------------------- /tapset/nginx/config.sxx: -------------------------------------------------------------------------------- 1 | // module nginx.config 2 | 3 | 4 | function ngx_cycle_get_module_main_conf(cycle, module_index) 5 | { 6 | $*cycle := @cast(cycle, "ngx_cycle_t", "$^exec_path") 7 | ctx = $*cycle->conf_ctx[@var("ngx_http_module", "$^exec_path")->index] 8 | if (ctx == 0) { 9 | return 0 10 | } 11 | $*ctx := @cast(ctx, "ngx_http_conf_ctx_t", "$^exec_path") 12 | return $*ctx->main_conf[module_index] 13 | } 14 | -------------------------------------------------------------------------------- /samples/ctx-switches.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | global count 4 | 5 | probe scheduler.ctxswitch { 6 | my_pid = target() 7 | if (prev_pid == my_pid || next_pid == my_pid) { 8 | count++ 9 | } 10 | } 11 | 12 | probe timer.s(1) { 13 | printf("[%d] %d cs/sec\n", gettimeofday_s(), count) 14 | count = 0 15 | } 16 | 17 | probe begin { 18 | warn(sprintf("Tracing process %d ($^exec_path).\nHit Ctrl-C to end.\n", target())) 19 | } 20 | -------------------------------------------------------------------------------- /samples/lj-trace-exit-rate.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # check how many times LuaJIT exits a compiled trace every second. 4 | 5 | global c 6 | 7 | probe process("$^libluajit_path").function("lj_trace_exit") { 8 | c++ 9 | } 10 | 11 | probe timer.s(1) { 12 | printf("trace exits: %d times/sec\n", c) 13 | c = 0 14 | } 15 | 16 | probe begin { 17 | printf("Start tracing %d ($^exec_path)...\nHit exit to end.\n\n", 18 | target()) 19 | } 20 | 21 | -------------------------------------------------------------------------------- /samples/ngx-rps.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Print out the current number of requests per second in the Nginx worker 4 | # process specified at real time. 5 | 6 | global count 7 | 8 | probe @pfunc(ngx_http_log_request) 9 | { 10 | if ($^pid_ok && $r == $r->main) { 11 | count++ 12 | } 13 | } 14 | 15 | probe timer.s(1) { 16 | printf("[%d] %d req/sec\n", gettimeofday_s(), count) 17 | count = 0 18 | } 19 | 20 | probe begin { 21 | warn(sprintf("Tracing process %d ($^exec_path).\nHit Ctrl-C to end.\n", target())) 22 | } 23 | -------------------------------------------------------------------------------- /tapset/nginx/lua.sxx: -------------------------------------------------------------------------------- 1 | // module nginx.config 2 | 3 | 4 | @use nginx.config 5 | 6 | 7 | function ngx_lua_cycle_get_main_conf() 8 | { 9 | return ngx_cycle_get_module_main_conf(@var("ngx_cycle", "$^exec_path"), 10 | @var("ngx_http_lua_module", "$^exec_path")->ctx_index) 11 | } 12 | 13 | 14 | function ngx_lua_get_main_lua_vm() 15 | { 16 | lmcf = ngx_lua_cycle_get_main_conf() 17 | if (lmcf) { 18 | $*lmcf := @cast(lmcf, "ngx_http_lua_main_conf_t", "$^exec_path") 19 | return $*lmcf->lua 20 | } 21 | return 0 22 | } 23 | -------------------------------------------------------------------------------- /tapset/nginx/chain.sxx: -------------------------------------------------------------------------------- 1 | // module nginx.chain 2 | 3 | 4 | function ngx_buf_in_memory(b) 5 | { 6 | $*b := @cast(b, "ngx_buf_t", "$^exec_path") 7 | return $*b->temporary || $*b->memory || $*b->mmap 8 | } 9 | 10 | 11 | function ngx_buf_size(b) 12 | { 13 | if (ngx_buf_in_memory(b)) { 14 | return $*b->last - $*b->pos 15 | } 16 | return $*b->file_last - $*b->file_pos 17 | } 18 | 19 | 20 | function ngx_chain_buf_size(cl) 21 | { 22 | $*cl := @cast(cl, "ngx_chain_t", "$^exec_path") 23 | sz = 0 24 | for (; cl; cl = $*cl->next) { 25 | sz += ngx_buf_size($*cl->buf) 26 | } 27 | return sz 28 | } 29 | -------------------------------------------------------------------------------- /samples/lj-bucket-depth-accessed.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use luajit 4 | 5 | global stats 6 | 7 | probe process("$^libluajit_path").statement("*@lj_str.c:161") { 8 | bucket = &$g->strhash[$h & $g->strmask] 9 | stats <<< luajit_bucket_depth(bucket) 10 | //printf("depth = %d\n", depth) 11 | //exit() 12 | } 13 | 14 | probe end { 15 | printf("Bucket depths accessed by lj_str_new (min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), @max(stats)) 16 | print(@hist_linear(stats, 0, 10, 1)) 17 | } 18 | 19 | probe begin 20 | { 21 | printf("Start tracing %d ($^exec_path)\n", target()) 22 | printf("Hit Ctrl-C to end.\n") 23 | } 24 | -------------------------------------------------------------------------------- /samples/epoll-et-lt.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @define EPOLL_CTL_ADD %( 1 %) 4 | @define EPOLLET %( 0x80000000 %) 5 | 6 | global ets, lts 7 | 8 | probe syscall.epoll_ctl { 9 | if (pid() == target() && $op == @EPOLL_CTL_ADD) { 10 | if (@cast($event, "epoll_event")->events & @EPOLLET) { 11 | ets++ 12 | 13 | } else { 14 | lts++ 15 | } 16 | } 17 | } 18 | 19 | probe timer.s(1) { 20 | printf("%d ET, %d LT.\n", ets, lts) 21 | ets = 0 22 | lts = 0 23 | } 24 | 25 | probe begin { 26 | printf("Tracing epoll_ctl in user process %d ($^exec_path)...\n", target()) 27 | printf("Hit Ctrl-C to end.\n") 28 | } 29 | -------------------------------------------------------------------------------- /tapset/nginx/pool.sxx: -------------------------------------------------------------------------------- 1 | // module nginx.pool 2 | 3 | function ngx_pool_size(pool) 4 | { 5 | sz = 0 6 | 7 | p = pool 8 | $*p := @cast(p, "ngx_pool_t", "$^exec_path") 9 | $*pool := @cast(pool, "ngx_pool_t", "$^exec_path") 10 | while (p) { 11 | sz += $*p->d->end - p 12 | p = $*p->d->next 13 | } 14 | 15 | $*large := @cast(large, "ngx_pool_large_t", "$^exec_path") 16 | for (large = $*pool->large; large; large = $*large->next) { 17 | ptr = $*large->alloc 18 | if (ptr) { 19 | ptr -= &@cast(0, "size_t")[1] 20 | sz += @cast(ptr, "size_t")[0] & ~(&@cast(0, "size_t")[1] - 1) 21 | } 22 | } 23 | 24 | return sz 25 | } 26 | -------------------------------------------------------------------------------- /samples/cpu-hogs.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | global stats, total 4 | 5 | probe timer.profile { 6 | total++ 7 | stats[execname()] <<< 1 8 | } 9 | 10 | probe begin { 11 | printf("Tracing the whole system...\n") 12 | %( "$^arg_time :default()" != "" %? 13 | printf("Please wait for $^arg_time seconds...\n") 14 | %: 15 | printf("Hit Ctrl-C to end.\n") 16 | %) 17 | } 18 | 19 | probe end { 20 | if (!total) { 21 | printf("\nNo samples found so far.\n") 22 | 23 | } else { 24 | printf("\n") 25 | foreach (name in stats- limit 20) { 26 | printf("%s %d%%\n", name, @count(stats[name]) * 100 / total) 27 | } 28 | } 29 | } 30 | 31 | 32 | %( "$^arg_time" != "" %? 33 | probe timer.s($^arg_time) { 34 | exit() 35 | } 36 | %) 37 | -------------------------------------------------------------------------------- /samples/epoll-loop-blocking.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # OPS-935 4 | # Measure the blocking effect in an event loop driven by epoll_wait. 5 | # 6 | # Accept the argument "limit" for the blocking time threshold, 7 | # which defaults to 200 (ms). 8 | 9 | global begin 10 | 11 | probe syscall.epoll_wait.return { 12 | if (target() == pid()) { 13 | begin = gettimeofday_ms() 14 | } 15 | } 16 | 17 | probe syscall.epoll_wait { 18 | if (target() == pid() && begin > 0) { 19 | elapsed = gettimeofday_ms() - begin 20 | if (elapsed >= $^arg_limit :default(200)) { 21 | printf("[%d] epoll loop blocked for %dms\n", 22 | gettimeofday_s(), elapsed) 23 | } 24 | } 25 | } 26 | 27 | probe begin { 28 | printf("Start tracing %d...\n", target()) 29 | } 30 | -------------------------------------------------------------------------------- /samples/ngx-open-file-cache-misses.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | global misses, total, in_ctx 4 | 5 | probe @pfunc(ngx_open_cached_file) { 6 | if (pid() == target()) { 7 | in_ctx = 1 8 | total++ 9 | } 10 | } 11 | 12 | probe @pfunc(ngx_open_cached_file).return { 13 | if (pid() == target()) { 14 | in_ctx = 0 15 | } 16 | } 17 | 18 | probe @pfunc(ngx_open_and_stat_file) { 19 | if (pid() == target() && in_ctx) { 20 | misses++ 21 | } 22 | } 23 | 24 | probe end { 25 | if (total == 0) { 26 | printf("no samples observed.\n") 27 | 28 | } else { 29 | printf("nginx open file cache miss rate: %d%%\n", misses * 100 / total) 30 | } 31 | } 32 | 33 | probe begin { 34 | warn(sprintf("Start tracing process %d...\nHit Ctrl-C to end.", target())) 35 | } 36 | -------------------------------------------------------------------------------- /samples/ngx-lua-count-timers.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* Copyright (C) Simon Eskildsen 4 | * Copyright (C) Yichun Zhang 5 | * */ 6 | 7 | @use nginx.lua 8 | 9 | probe @pfunc(ngx_process_events_and_timers), 10 | @pfunc(ngx_http_handler), 11 | @pfunc(ngx_http_lua_timer_handler) 12 | { 13 | if (pid() == target()) { 14 | lmcf = ngx_lua_cycle_get_main_conf() 15 | $*lmcf := @cast(lmcf, "ngx_http_lua_main_conf_t") 16 | 17 | println("Running timers: ", $*lmcf->running_timers) 18 | println("Max running timers: ", $*lmcf->max_running_timers) 19 | println("Pending timers: ", $*lmcf->pending_timers) 20 | println("Max pending timers: ", $*lmcf->max_pending_timers) 21 | exit() 22 | } 23 | } 24 | 25 | probe begin { 26 | printf("Start tracing %d ($^exec_path)...\n", target()) 27 | } 28 | -------------------------------------------------------------------------------- /samples/ngx-lj-gc-speed.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit 5 | 6 | global old, G, freed 7 | 8 | probe process("$^liblua_path").function("lj_gc_step") 9 | { 10 | if (G == 0) { 11 | L = ngx_lua_get_main_lua_vm() 12 | //printf("L = %p\n", L) 13 | G = luajit_G(L) 14 | } 15 | 16 | $*G := @cast(G, "global_State", "$^libluajit_path") 17 | old = $*G->gc->total 18 | } 19 | 20 | probe process("$^liblua_path").function("lj_gc_step").return 21 | { 22 | if (old && G) { 23 | freed += old - $*G->gc->total 24 | } 25 | } 26 | 27 | probe timer.s(1) { 28 | if (target() == pid()) { 29 | printf("LuaJIT GC freed: %d bytes/sec\n", freed) 30 | freed = 0 31 | } 32 | } 33 | 34 | probe begin { 35 | printf("Start tracing %d ($^exec_path)\nHit Ctrl-C to end.\n", target()) 36 | } 37 | -------------------------------------------------------------------------------- /samples/unix-stream-socks.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use kernel.socket 4 | 5 | global count 6 | 7 | probe syscall.$^arg_call :default(recvfrom).return { 8 | if (pid() == target()) { 9 | socket = sockfd_lookup($fd) 10 | if (socket_unix_stream_file_name(socket) == "$^arg_file") { 11 | ret = $return 12 | count++ 13 | 14 | %( "$^arg_ret :default()" != "" %? 15 | if (ret == $^arg_ret) { 16 | printf("$^arg_call returns %d\n", ret) 17 | } 18 | %: 19 | if (ret <= 0) { 20 | printf("$^arg_call returns %d\n", ret) 21 | } 22 | %) 23 | } 24 | } 25 | } 26 | 27 | probe begin { 28 | printf("Tracing process %d ($^exec_path).\nHit Ctrl-C to end.\n", target()) 29 | } 30 | 31 | probe end { 32 | printf("Found %d hit(s).\n", count) 33 | } 34 | -------------------------------------------------------------------------------- /samples/ngx-upstream-err-log.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # OPS-935 4 | # Analyze the state in the position generating the 5 | # "upstream prematurely closed connection while reading response header" 6 | 7 | 8 | @use kernel.socket, nginx.upstream 9 | 10 | 11 | probe process("$^exec_path").statement("*@ngx_http_upstream.c:1762") 12 | //probe process("$^exec_path").statement("ngx_http_upstream_send_request") 13 | { 14 | u = $r->upstream 15 | $*u := @cast(u, "ngx_http_upstream_t") 16 | 17 | printf("hit: request_sent:%d, elapsed:%dms", $*u->request_sent, 18 | ngx_u_resp_time(u)) 19 | 20 | fd = $*u->peer->connection->fd 21 | socket = sockfd_lookup(fd) 22 | printf(", tcp-state:%s\n", socket_tcp_state(socket)) 23 | } 24 | 25 | 26 | probe begin 27 | { 28 | warn(sprintf("Start tracing user process %d...\nHit Ctrl to end.", target())) 29 | } 30 | -------------------------------------------------------------------------------- /samples/slow-vfs-reads.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Dump the latency distribution of any function-like probe specified by 4 | # the --arg func=FUNC option. 5 | 6 | global begin 7 | 8 | probe vfs.read { 9 | if (pid() == target()) { 10 | begin = gettimeofday_ms() 11 | if (func == "") { 12 | func = probefunc() 13 | } 14 | } 15 | } 16 | 17 | probe vfs.read.return { 18 | if (pid() == target() && begin > 0) { 19 | elapsed = gettimeofday_ms() - begin 20 | if (elapsed > $^arg_limit :default(100)) { 21 | printf("[%d] latency=%dms dev=%s bytes_read=%d err=%d errstr=%s\n", 22 | gettimeofday_s(), elapsed, devname, bytes_read, 23 | error, error_str) 24 | } 25 | } 26 | } 27 | 28 | probe begin { 29 | printf("Start tracing %d...\nHit Ctrl-C to end.\n\n", target()) 30 | } 31 | -------------------------------------------------------------------------------- /samples/lj-lua-bt.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit 5 | 6 | function process() { 7 | if (@defined(@var("globalL", "$^exec_path"))) { 8 | mL = @var("globalL", "$^exec_path") 9 | 10 | } else { 11 | mL = ngx_lua_get_main_lua_vm() 12 | } 13 | 14 | if (mL == 0) { 15 | return 0 16 | } 17 | 18 | g = luajit_G(mL) 19 | if (g == 0) { 20 | return 0 21 | } 22 | 23 | L = luajit_cur_thread(g) 24 | if (L == 0) { 25 | return 0 26 | } 27 | 28 | bt = luajit_backtrace(L, g, 0) 29 | if (bt != "") { 30 | print(bt) 31 | return 1 32 | } 33 | 34 | return 0 35 | } 36 | 37 | probe timer.profile 38 | { 39 | if (pid() == target()) { 40 | if (process()) { 41 | exit() 42 | } 43 | } 44 | } 45 | 46 | probe begin 47 | { 48 | printf("Start tracing %d ($^exec_path)\n", target()) 49 | } 50 | -------------------------------------------------------------------------------- /samples/lj-find-str.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit 5 | 6 | function process() { 7 | if (@defined(@var("globalL", "$^exec_path"))) { 8 | mL = @var("globalL", "$^exec_path") 9 | 10 | } else { 11 | mL = ngx_lua_get_main_lua_vm() 12 | } 13 | 14 | if (mL == 0) { 15 | return 0 16 | } 17 | 18 | g = luajit_G(mL) 19 | if (g == 0) { 20 | return 0 21 | } 22 | 23 | gcs = luajit_find_gcstr(g, "$^arg_str") 24 | if (gcs) { 25 | printf("Found: (GCstr*)%p\n", gcs) 26 | 27 | } else { 28 | printf("Not found.\n") 29 | } 30 | 31 | return 1 32 | } 33 | 34 | probe @pfunc(ngx_process_events_and_timers)?, timer.profile 35 | { 36 | if (pid() == target()) { 37 | if (process()) { 38 | exit() 39 | } 40 | } 41 | } 42 | 43 | probe begin 44 | { 45 | printf("Start tracing %d ($^exec_path)\n", target()) 46 | } 47 | -------------------------------------------------------------------------------- /samples/lj-gc.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit 5 | 6 | probe @pfunc(ngx_process_events_and_timers)?, timer.profile 7 | { 8 | if (pid() == target()) { 9 | if (@defined(@var("globalL", "$^exec_path"))) { 10 | L = @var("globalL", "$^exec_path") 11 | 12 | } else { 13 | L = ngx_lua_get_main_lua_vm() 14 | } 15 | 16 | if (L == 0) { 17 | printf("Failed to get the main Lua VM\n") 18 | exit() 19 | } 20 | 21 | process(L) 22 | } 23 | } 24 | 25 | function process(L) 26 | { 27 | //printf("L = %p\n", L) 28 | G = luajit_G(L) 29 | if (G == 0) { 30 | printf("Failed to get the global Lua state\n") 31 | exit() 32 | 33 | } else { 34 | $*G := @cast(G, "global_State", "$^libluajit_path") 35 | printf("Total GC count: %d bytes\n", $*G->gc->total) 36 | exit() 37 | } 38 | } 39 | 40 | probe begin { 41 | printf("Start tracing %d ($^exec_path)\n", target()) 42 | } 43 | -------------------------------------------------------------------------------- /samples/ngx-lua-useless-cosockets.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | global acquired, unused 4 | 5 | probe @pfunc(ngx_http_lua_get_keepalive_peer) { 6 | if (pid() == target()) { 7 | acquired[$u] = 1 8 | } 9 | } 10 | 11 | probe @pfunc(ngx_http_lua_socket_tcp_read), 12 | @pfunc(ngx_http_lua_socket_send) 13 | { 14 | if (pid() == target() && acquired[$u]) { 15 | delete acquired[$u] 16 | } 17 | } 18 | 19 | probe @pfunc(ngx_http_lua_socket_tcp_finalize) 20 | { 21 | if (pid() == target() && acquired[$u]) { 22 | unused++ 23 | } 24 | } 25 | 26 | probe end { 27 | printf("\nFound %d unused ngx_lua TCP cosockets.\n", unused) 28 | } 29 | 30 | probe begin 31 | { 32 | printf("Tracing process %d ($^exec_path)\n", target()) 33 | %( "$^arg_time :default()" != "" %? 34 | printf("Please wait for $^arg_time seconds...\n") 35 | %: 36 | printf("Hit Ctrl-C to end.\n") 37 | %) 38 | } 39 | 40 | %( "$^arg_time" != "" %? 41 | probe timer.s($^arg_time) { 42 | exit() 43 | } 44 | %) 45 | -------------------------------------------------------------------------------- /samples/ngx-upstream-next.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Print out attempts to retry server connect(). 4 | 5 | global in_next, ft_type, peer_name, schema 6 | 7 | probe @pfunc(ngx_http_upstream_next) { 8 | if (target() == pid()) { 9 | in_next = 1 10 | ft_type = $ft_type 11 | peer_name = user_string_n($u->peer->name->data, $u->peer->name->len) 12 | schema = user_string_n($u->schema->data, $u->schema->len) 13 | } 14 | } 15 | 16 | probe @pfunc(ngx_http_upstream_next).return { 17 | if (target() == pid()) { 18 | in_next = 0 19 | ft_type = 0 20 | } 21 | } 22 | 23 | probe @pfunc(ngx_http_upstream_connect) { 24 | if (target() == pid() && in_next) { 25 | printf("retrying: oldpeer=\"%s%s\" ft_type=0x%x status=%d tries=%d\n", 26 | schema, 27 | peer_name, 28 | ft_type, $u->state->status, $u->peer->tries) 29 | } 30 | } 31 | 32 | probe begin { 33 | warn(sprintf("Tracing process %d.\nHit Ctrl-C to end.\n", target())) 34 | } 35 | -------------------------------------------------------------------------------- /samples/epoll-loop-blocking-distr.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Measure the distribution of the blocking latency in an event loop 4 | # driven by epoll_wait. 5 | 6 | global begin, stats 7 | 8 | probe syscall.epoll_wait.return { 9 | if (target() == pid()) { 10 | begin = gettimeofday_ms() 11 | } 12 | } 13 | 14 | probe syscall.epoll_wait { 15 | if (target() == pid() && begin > 0) { 16 | stats <<< gettimeofday_ms() - begin 17 | } 18 | } 19 | 20 | probe timer.s($^arg_time :default(5)) { 21 | exit() 22 | } 23 | 24 | probe end { 25 | if (begin == 0) { 26 | printf("No samples observed so far.\n"); 27 | 28 | } else { 29 | printf("Distribution of epoll loop blocking latencies (in milliseconds)\n") 30 | printf("max/avg/min: %d/%d/%d\n", @max(stats), @avg(stats), @min(stats)) 31 | print(@hist_log(stats)) 32 | } 33 | } 34 | 35 | probe begin { 36 | printf("Start tracing %d ($^exec_path)...\nPlease wait for %d seconds.\n\n", 37 | target(), $^arg_time) 38 | } 39 | -------------------------------------------------------------------------------- /samples/luajit21-gc64/lj-gc.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit_gc64 5 | 6 | probe @pfunc(ngx_process_events_and_timers)?, timer.profile 7 | { 8 | if (pid() == target()) { 9 | if (@defined(@var("globalL", "$^exec_path"))) { 10 | L = @var("globalL", "$^exec_path") 11 | 12 | } else { 13 | L = ngx_lua_get_main_lua_vm() 14 | } 15 | 16 | if (L == 0) { 17 | printf("Failed to get the main Lua VM\n") 18 | exit() 19 | } 20 | 21 | process(L) 22 | } 23 | } 24 | 25 | function process(L) 26 | { 27 | //printf("L = %p\n", L) 28 | G = luajit_G(L) 29 | if (G == 0) { 30 | printf("Failed to get the global Lua state\n") 31 | exit() 32 | 33 | } else { 34 | $*G := @cast(G, "global_State", "$^libluajit_path") 35 | printf("Total GC count: %lu bytes\n", $*G->gc->total) 36 | exit() 37 | } 38 | } 39 | 40 | probe begin { 41 | printf("Start tracing %d ($^exec_path)\n", target()) 42 | } 43 | -------------------------------------------------------------------------------- /samples/ngx-req-pool-size.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Copyright (C) Yichun Zhang 4 | 5 | @use nginx.pool 6 | 7 | global stats 8 | 9 | probe @pfunc(ngx_http_free_request).callee("ngx_destroy_pool") 10 | { 11 | //printf("%p\n", $pool) 12 | stats <<< ngx_pool_size($pool) 13 | } 14 | 15 | probe end { 16 | count = @count(stats) 17 | if (count == 0) { 18 | printf("\nNo samples found so far.\n") 19 | 20 | } else { 21 | printf("\nDistribution of nginx request pool sizes for %d samples:\n", 22 | count) 23 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 24 | @max(stats)) 25 | print(@hist_log(stats)) 26 | } 27 | } 28 | 29 | probe begin { 30 | printf("Start tracing process %d ($^exec_path)...\n", target()) 31 | %( "$^arg_time :default()" != "" %? 32 | printf("Please wait for $^arg_time seconds...\n") 33 | %: 34 | printf("Hit Ctrl-C to end.\n") 35 | %) 36 | } 37 | 38 | %( "$^arg_time" != "" %? 39 | probe timer.s($^arg_time) { 40 | exit() 41 | } 42 | %) 43 | -------------------------------------------------------------------------------- /samples/cpu-robbers.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | global robbers, total 4 | 5 | probe scheduler.ctxswitch { 6 | my_pid = target() 7 | if (prev_pid == my_pid && next_pid != 0 && next_pid != my_pid) { 8 | name = pid2execname(next_pid) 9 | if (name == "") { 10 | name = sprintf("%d", next_pid) 11 | } 12 | robbers[name] <<< 1 13 | total++ 14 | } 15 | } 16 | 17 | probe end { 18 | printf("\n") 19 | i = 0 20 | foreach (robber in robbers- limit $^arg_limit :default(20)) { 21 | i++ 22 | cnt = @count(robbers[robber]) 23 | printf("#%d %s: %d%% (%d samples)\n", i, robber, cnt * 100 / total, cnt) 24 | } 25 | if (i == 0) { 26 | printf("No CPU time robbers found so far.\n") 27 | } 28 | } 29 | 30 | probe begin { 31 | printf("Start tracing process %d ($^exec_path)...\n", target()) 32 | %( "$^arg_time :default()" != "" %? 33 | printf("Please wait for $^arg_time seconds...\n") 34 | %: 35 | printf("Hit Ctrl-C to end.\n") 36 | %) 37 | } 38 | 39 | %( "$^arg_time" != "" %? 40 | probe timer.s($^arg_time) { 41 | exit() 42 | } 43 | %) 44 | -------------------------------------------------------------------------------- /samples/ngx-sr-trunc.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.chain 4 | @use nginx.request 5 | @use nginx.upstream 6 | 7 | global size 8 | 9 | probe @pfunc(ngx_http_lua_post_subrequest) 10 | { 11 | $*data := @cast($data, "ngx_http_lua_post_subrequest_data_t") 12 | if (!$*data->ctx->seen_last_for_subreq) { 13 | r = $r 14 | $*r := @cast(r, "ngx_http_request_t") 15 | size += ngx_chain_buf_size($*data->ctx->body) 16 | printf("%s %s?%s: host: %s, ua: %s body size: %d, content-length: %d, cached: %d\n", 17 | ngx_req_method(r), ngx_req_uri(r), ngx_req_args(r), 18 | ngx_req_host(r), ngx_req_ua(r), 19 | size, $*r->headers_out->content_length_n, $*r->cached) 20 | u = $*r->upstream 21 | $*u := @cast(u, "ngx_http_upstream_t") 22 | if (u) { 23 | printf(" upstream resp-time=%s (ms), resp-len=%s (bytes), status=%s, addr=%s\n", 24 | ngx_u_resp_times(r), ngx_u_resp_lens(r), 25 | ngx_u_resp_statuses(r), ngx_u_addr(r)) 26 | } 27 | } 28 | } 29 | 30 | probe begin { 31 | printf("Tracing %d ($^exec_path)\n\n", target()) 32 | } 33 | -------------------------------------------------------------------------------- /samples/sample-bt.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global bts; 8 | global quit = 0 9 | 10 | probe timer.profile { 11 | if ($^pid_ok) { 12 | %( "$^arg_execname :default()" != "" %? 13 | if (execname() == "$^arg_execname") { 14 | %) 15 | 16 | if (!quit) { 17 | bts[ubacktrace()] <<< 1 18 | 19 | } else { 20 | 21 | foreach (usr in bts- limit $^arg_limit :default(1000)) { 22 | print_ustack(usr) 23 | printf("\t%d\n", @count(bts[usr])) 24 | } 25 | 26 | exit() 27 | } 28 | 29 | %( "$^arg_execname :default()" != "" %? 30 | } 31 | %) 32 | } 33 | } 34 | 35 | probe timer.s($^arg_time) { 36 | nstacks = 0 37 | foreach (bt in bts limit 1) { 38 | nstacks++ 39 | } 40 | 41 | if (nstacks == 0) { 42 | warn("No backtraces found. Quitting now...\n") 43 | exit() 44 | 45 | } else { 46 | warn("Time's up. Quitting now...(it may take a while)\n") 47 | quit = 1 48 | } 49 | } 50 | 51 | probe begin { 52 | warn(sprintf("Start tracing process $^target ($^exec_path)...\n")) 53 | } 54 | -------------------------------------------------------------------------------- /samples/func-latency-distr.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Dump the latency distribution of any function-like probe specified by 4 | # the --arg func=FUNC option. 5 | 6 | global begin, stats, func 7 | 8 | probe $^arg_func { 9 | if (pid() == target()) { 10 | begin = gettimeofday_ns() 11 | if (func == "") { 12 | func = probefunc() 13 | } 14 | } 15 | } 16 | 17 | probe $^arg_func.return { 18 | if (pid() == target() && begin > 0) { 19 | stats <<< gettimeofday_ns() - begin 20 | } 21 | } 22 | 23 | probe end { 24 | if (begin == 0) { 25 | printf("No samples observed so far.\n"); 26 | 27 | } else { 28 | printf("Distribution of %s latencies (in nanoseconds) for %d samples\n", func, @count(stats)) 29 | printf("max/avg/min: %d/%d/%d\n", @max(stats), @avg(stats), @min(stats)) 30 | print(@hist_log(stats)) 31 | } 32 | } 33 | 34 | probe begin { 35 | printf("Start tracing %d ($^exec_path)\n", target()) 36 | %( "$^arg_time :default()" != "" %? 37 | printf("Please wait for $^arg_time seconds...\n") 38 | %: 39 | printf("Hit Ctrl-C to end.\n") 40 | %) 41 | } 42 | 43 | %( "$^arg_time" != "" %? 44 | probe timer.s($^arg_time) { 45 | exit() 46 | } 47 | %) 48 | -------------------------------------------------------------------------------- /samples/zlib-deflate-chunk-size.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global stats 8 | 9 | probe @pfunc(deflate)!, 10 | process("$^libz_path").function("deflate") 11 | { 12 | //warn("HERE") 13 | stats <<< $strm->avail_in 14 | } 15 | 16 | probe @pfunc(deflateEnd)!, 17 | process("$^libz_path").function("deflateEnd") 18 | { 19 | //warn("HERE") 20 | stats <<< $strm->avail_in 21 | } 22 | 23 | 24 | probe begin { 25 | printf("Start tracing process %d ($^exec_path)...\n", target()) 26 | %( "$^arg_time :default()" != "" %? 27 | printf("Please wait for $^arg_time seconds...\n") 28 | %: 29 | printf("Hit Ctrl-C to end.\n") 30 | %) 31 | } 32 | 33 | %( "$^arg_time" != "" %? 34 | probe timer.s($^arg_time) { 35 | exit() 36 | } 37 | %) 38 | 39 | probe end { 40 | count = @count(stats) 41 | if (count == 0) { 42 | printf("\nNo samples found so far.\n") 43 | 44 | } else { 45 | printf("\nDistribution of zlib deflate chunk sizes (in bytes) for %d samples:\n", 46 | count) 47 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 48 | @max(stats)) 49 | print(@hist_log(stats)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /samples/ngx-count-conns.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* Copyright (C) Simon Eskildsen 4 | * Copyright (C) Yichun Zhang 5 | * */ 6 | 7 | probe @pfunc(ngx_process_events_and_timers), @pfunc(ngx_http_handler) 8 | { 9 | if (pid() == target()) { 10 | $*cycle := @var("ngx_cycle@ngx_cycle.c") 11 | 12 | println("\n====== CONNECTIONS ======") 13 | 14 | printf("Max connections: %d\n", $*cycle->connection_n) 15 | printf("Free connections: %d\n", $*cycle->free_connection_n) 16 | printf("Used connections: %d\n", $*cycle->connection_n - $*cycle->free_connection_n) 17 | 18 | println("\n====== FILES ======") 19 | 20 | lim = $*cycle->files_n; 21 | if (lim == 0) { 22 | lim = task_rlimit_nofile(task_current()) 23 | } 24 | 25 | printf("Max files: %d\n", lim) 26 | 27 | n = $*cycle->open_files->part->nelts 28 | $*part := @cast(part, "ngx_list_part_t") 29 | for (part = $*cycle->open_files->part->next; part; part = $*part->next) { 30 | n += $*part->nelts 31 | } 32 | 33 | printf("Open normal files: %d\n", $*cycle->open_files->part->nelts) 34 | exit() 35 | } 36 | } 37 | 38 | probe begin { 39 | printf("Start tracing %d ($^exec_path)...\n", target()) 40 | } 41 | -------------------------------------------------------------------------------- /samples/ngx-upstream-post-conn.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # OPS-935 4 | # Analyze the latency distribution between upstream connect() and 5 | # the next upstream processing handler. 6 | 7 | global connecting 8 | global stats 9 | global hits 10 | 11 | probe @pfunc(ngx_http_upstream_connect) 12 | { 13 | connecting[$r] = gettimeofday_us() 14 | } 15 | 16 | probe @pfunc(ngx_http_upstream_finalize_request), 17 | @pfunc(ngx_http_upstream_send_request), 18 | @pfunc(ngx_http_upstream_next), 19 | @pfunc(ngx_http_upstream_process_header) 20 | { 21 | begin = connecting[$r] 22 | if (begin) { 23 | hits++ 24 | stats[probefunc()] <<< gettimeofday_us() - begin 25 | delete connecting[$r] 26 | } 27 | } 28 | 29 | probe end 30 | { 31 | printf("\n") 32 | 33 | if (hits == 0) { 34 | println("No hits found so far.") 35 | 36 | } else { 37 | 38 | printf("Logarithmic histogram for latency (us) between ngx_http_upstream_connect and the following upstream functions:\n") 39 | foreach (func in stats- limit $^arg_limit :default(1000)) { 40 | printf("* %s\n", func) 41 | print(@hist_log(stats[func])) 42 | } 43 | } 44 | } 45 | 46 | probe begin 47 | { 48 | warn(sprintf("Tracing user process %d ($^exec_path)...\nHit Ctrl-C to end.\n", target())) 49 | } 50 | -------------------------------------------------------------------------------- /samples/ngx-slow-purge-reqs.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.request 4 | global begins 5 | global send_header, send_req, process_header 6 | global in_ctx 7 | 8 | probe @pfunc(ngx_http_handler) { 9 | if ($r == $r->main && $r->method_name->len == 5) { 10 | m = ngx_req_method($r) 11 | if (m == "PURGE") { 12 | begins[$r] = gettimeofday_ms() 13 | } 14 | } 15 | } 16 | 17 | probe @pfunc(ngx_http_upstream_process_header) { 18 | if (begins[$r]) { 19 | process_header[$r] = gettimeofday_ms() 20 | in_ctx = 1 21 | } 22 | } 23 | 24 | probe @pfunc(ngx_http_log_request) { 25 | b = begins[$r] 26 | if (b) { 27 | delete begins[$r] 28 | 29 | snd_hd = send_header[$r] 30 | delete send_header[$r] 31 | 32 | prc_hd = process_header[$r] 33 | delete process_header[$r] 34 | 35 | req = send_req[$r] 36 | delete send_req[$r] 37 | 38 | printf("%s %s (total: %dms, upstream send req: %dms, upsream process header: %dms, send response header: %dms)\n", 39 | ngx_req_method($r), ngx_req_uri($r), gettimeofday_ms() - b, 40 | req ? req - b : -1, 41 | prc_hd ? prc_hd - b : -1, 42 | snd_hd ? snd_hd - b : -1) 43 | } 44 | } 45 | 46 | probe @pfunc(ngx_http_upstream_send_request) { 47 | if (begins[$r]) { 48 | send_req[$r] = gettimeofday_ms() 49 | } 50 | } 51 | 52 | probe @pfunc(ngx_http_header_filter) { 53 | if (begins[$r]) { 54 | send_header[$r] = gettimeofday_ms() 55 | } 56 | } 57 | 58 | probe begin { 59 | printf("Tracing %d ($^exec_path)\n", target()) 60 | } 61 | -------------------------------------------------------------------------------- /samples/ngx-lua-udp-recv-time.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global active_reqs 8 | global recv_begin 9 | global stats 10 | 11 | probe @pfunc(ngx_http_process_request) 12 | { 13 | active_reqs[$r] = 1 14 | } 15 | 16 | probe @pfunc(ngx_http_lua_socket_udp_read) 17 | { 18 | r = $r 19 | u = $u 20 | if (active_reqs[r]) { 21 | if (!recv_begin[r, u]) { 22 | recv_begin[r, u] = gettimeofday_us() 23 | } 24 | } 25 | } 26 | 27 | probe @pfunc(ngx_http_log_request) 28 | { 29 | delete active_reqs[$r] 30 | } 31 | 32 | probe @pfunc(ngx_http_lua_socket_udp_receive_retval_handler) 33 | { 34 | r = $r 35 | u = $u 36 | begin = recv_begin[r, u] 37 | if (begin) { 38 | stats <<< gettimeofday_us() - begin 39 | } 40 | delete recv_begin[r, u] 41 | } 42 | 43 | probe begin { 44 | printf("Start tracing process %d ($^exec_path)...\n", target()) 45 | %( "$^arg_time :default()" != "" %? 46 | printf("Please wait for $^arg_time seconds...\n") 47 | %: 48 | printf("Hit Ctrl-C to end.\n") 49 | %) 50 | } 51 | 52 | %( "$^arg_time" != "" %? 53 | probe timer.s($^arg_time) { 54 | exit() 55 | } 56 | %) 57 | 58 | probe end { 59 | count = @count(stats) 60 | if (count == 0) { 61 | printf("\nNo samples found so far.\n") 62 | 63 | } else { 64 | printf("\nDistribution of the ngx_lua ngx.socket.udp receive latencies (in microseconds) for %d samples:\n", 65 | count) 66 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 67 | @max(stats)) 68 | print(@hist_log(stats)) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /samples/openssl-handshake-diagnosis.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | global cipher_stats 4 | global counted_session 5 | global aesni_used = 0 6 | 7 | probe begin { 8 | printf("Start tracing %d ($^exec_path)...\n", target()) 9 | %( "$^arg_time" != "" %? 10 | printf("Please wait for $^arg_time seconds...\n") 11 | %: 12 | printf("Hit Ctrl-C to end.\n") 13 | %) 14 | } 15 | 16 | %( "$^arg_time :default()" != "" %? 17 | probe timer.s($^arg_time) { 18 | exit() 19 | } 20 | %) 21 | 22 | probe end { 23 | println("OpenSSL handshake disgnosis:") 24 | println("AES-NI:") 25 | if (aesni_used) { 26 | println("\ton") 27 | } else { 28 | println("\toff") 29 | } 30 | 31 | println("cipher usage:") 32 | foreach (name in cipher_stats-) { 33 | printf("\t%-32s\t%d\n", name, cipher_stats[name]) 34 | } 35 | } 36 | 37 | probe process("$^libcrypto_path").function("aesni_*") 38 | { 39 | if (target() == pid()) { 40 | aesni_used = 1 41 | } 42 | } 43 | 44 | probe process("$^libssl_path").function("SSL_do_handshake").return 45 | { 46 | if (target() == pid()) { 47 | session = @entry(@var("s")->session) 48 | if (session) { 49 | if (!counted_session[session]) { 50 | cipher = @cast(session, "ssl_session_st", "$^libssl_path")->cipher 51 | if (cipher) { 52 | $*cipher := @cast(cipher, "ssl_cipher_st", "$^libssl_path") 53 | name = user_string($*cipher->name) 54 | cipher_stats[name]++ 55 | 56 | counted_session[session] = 1; 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /samples/ngx-orig-resp-body-len.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global resp_sizes 8 | global stats 9 | 10 | probe @pfunc(ngx_http_charset_body_filter)!, 11 | @pfunc(ngx_http_postpone_filter)!, 12 | @pfunc(ngx_http_gzip_body_filter)!, 13 | @pfunc(ngx_http_chunked_body_filter) 14 | { 15 | r = $r 16 | if (r == $r->main && $in) { 17 | size = 0 18 | $*cl := @cast(cl, "ngx_chain_t") 19 | for (cl = $in; cl; cl = $*cl->next) { 20 | b = $*cl->buf 21 | $*b := @cast(b, "ngx_buf_t") 22 | size += $*b->last - $*b->pos 23 | } 24 | resp_sizes[r] += size 25 | } 26 | } 27 | 28 | probe @pfunc(ngx_http_free_request) 29 | { 30 | r = $r 31 | if (r == $r->main) { 32 | stats <<< resp_sizes[r] 33 | delete resp_sizes[r] 34 | } 35 | } 36 | 37 | probe begin { 38 | printf("Start tracing process %d ($^exec_path)...\n", target()) 39 | %( "$^arg_time :default()" != "" %? 40 | printf("Please wait for $^arg_time seconds...\n") 41 | %: 42 | printf("Hit Ctrl-C to end.\n") 43 | %) 44 | } 45 | 46 | %( "$^arg_time" != "" %? 47 | probe timer.s($^arg_time) { 48 | exit() 49 | } 50 | %) 51 | 52 | probe end { 53 | count = @count(stats) 54 | if (count == 0) { 55 | printf("\nNo samples found so far.\n") 56 | 57 | } else { 58 | printf("\nDistribution of original response body sizes (in bytes) for %d samples:\n", 59 | count) 60 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 61 | @max(stats)) 62 | print(@hist_log(stats)) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /samples/epoll-loop-blocking-vfs.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # OPS-935 4 | # Measure the blocking effect in an event loop driven by epoll_wait and 5 | # also check how much VFS (file IO) latency is contributed to each blocking period. 6 | # 7 | # Accept the argument "limit" for the blocking time threshold, 8 | # which defaults to 200 (ms). 9 | 10 | 11 | global epoll_begin 12 | global vfs_begin 13 | global vfs_latency 14 | 15 | probe syscall.lseek, syscall.renameat, syscall.open, syscall.close, syscall.sendfile*, 16 | vfs.read*, vfs.write*, syscall.*stat*, syscall.unlink, syscall.mkdir, syscall.rmdir 17 | { 18 | if (target() == pid()) { 19 | vfs_begin = gettimeofday_us() 20 | } 21 | } 22 | 23 | probe syscall.lseek.return, syscall.renameat.return, syscall.open.return, syscall.close.return, syscall.sendfile*.return, 24 | vfs.read*.return, vfs.write*.return, syscall.*stat*.return, syscall.unlink.return, syscall.mkdir.return, syscall.rmdir.return 25 | { 26 | if (target() == pid() && vfs_begin) { 27 | vfs_latency += gettimeofday_us() - vfs_begin 28 | vfs_begin = 0 29 | } 30 | } 31 | 32 | probe syscall.epoll_wait.return { 33 | if (target() == pid()) { 34 | epoll_begin = gettimeofday_ms() 35 | vfs_latency = 0 36 | vfs_begin = 0 37 | } 38 | } 39 | 40 | probe syscall.epoll_wait { 41 | if (target() == pid() && epoll_begin > 0) { 42 | elapsed = gettimeofday_ms() - epoll_begin 43 | if (elapsed > $^arg_limit :default(200)) { 44 | printf("[%d] epoll loop blocked for %dms (file IO: %dms)\n", 45 | gettimeofday_s(), elapsed, vfs_latency / 1000) 46 | } 47 | } 48 | } 49 | 50 | probe begin { 51 | printf("Start tracing %d...\n", target()) 52 | } 53 | -------------------------------------------------------------------------------- /samples/ngx-lua-tcp-recv-time.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global active_reqs 8 | global recv_begin 9 | global stats 10 | 11 | probe @pfunc(ngx_http_process_request) 12 | { 13 | active_reqs[$r] = 1 14 | } 15 | 16 | probe @pfunc(ngx_http_lua_socket_tcp_read) 17 | { 18 | r = $r 19 | u = $u 20 | if (active_reqs[r]) { 21 | if (!recv_begin[r, u]) { 22 | recv_begin[r, u] = gettimeofday_us() 23 | } 24 | } 25 | } 26 | 27 | probe @pfunc(ngx_http_log_request) 28 | { 29 | delete active_reqs[$r] 30 | } 31 | 32 | probe @pfunc(ngx_http_lua_socket_tcp_finalize) 33 | { 34 | delete recv_begin[$r, $u] 35 | } 36 | 37 | probe @pfunc(ngx_http_lua_socket_tcp_receive_retval_handler) 38 | { 39 | r = $r 40 | u = $u 41 | begin = recv_begin[r, u] 42 | if (begin) { 43 | stats <<< gettimeofday_us() - begin 44 | } 45 | delete recv_begin[r, u] 46 | } 47 | 48 | probe begin { 49 | printf("Start tracing process %d ($^exec_path)...\n", target()) 50 | %( "$^arg_time :default()" != "" %? 51 | printf("Please wait for $^arg_time seconds...\n") 52 | %: 53 | printf("Hit Ctrl-C to end.\n") 54 | %) 55 | } 56 | 57 | %( "$^arg_time" != "" %? 58 | probe timer.s($^arg_time) { 59 | exit() 60 | } 61 | %) 62 | 63 | probe end { 64 | count = @count(stats) 65 | if (count == 0) { 66 | printf("\nNo samples found so far.\n") 67 | 68 | } else { 69 | printf("\nDistribution of the ngx_lua ngx.socket.tcp receive latencies (in microseconds) for %d samples:\n", 70 | count) 71 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 72 | @max(stats)) 73 | print(@hist_log(stats)) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tapset/kernel/socket.sxx: -------------------------------------------------------------------------------- 1 | // module kernel.socket 2 | 3 | function sockfd_lookup(fd) 4 | { 5 | current = task_current() 6 | $*current := @cast(current, "task_struct", "kernel") 7 | 8 | files = $*current->files 9 | $*files := @cast(files, "files_struct", "kernel") 10 | 11 | if (files == 0 || $*files->fdt == 0 || fd >= $*files->fdt->max_fds) { 12 | return 0 13 | } 14 | 15 | file = $*files->fdt->fd[fd] 16 | $*file := @cast(file, "struct file", "kernel") 17 | 18 | if ($*file->f_op != &@var("socket_file_ops@net/socket.c", "kernel")) { 19 | return 0 20 | } 21 | 22 | return $*file->private_data 23 | } 24 | 25 | 26 | function socket_unix_stream_file_name(socket) 27 | { 28 | $*socket := @cast(socket, "struct socket", "kernel") 29 | //printf("ops = %p\n", $*socket->ops) 30 | //printf("&unix_stream_ops = %p\n", &@var("unix_stream_ops@af_unix.c", "kernel")) 31 | if ($*socket->ops == &@var("unix_stream_ops@af_unix.c", "kernel")) { 32 | u = $*socket->sk 33 | $*u := @cast(u, "struct unix_sock", "kernel") 34 | //printf("MATCH! %s\n", kernel_string($*u->path->dentry->d_iname)) 35 | if ($*u->path->dentry) { 36 | return kernel_string($*u->path->dentry->d_iname) 37 | } 38 | } 39 | 40 | return "" 41 | } 42 | 43 | 44 | function socket_tcp_state(socket) 45 | { 46 | state = $*socket->sk->__sk_common->skc_state 47 | return tcp_sockstate_str(state) 48 | } 49 | 50 | 51 | function socket_dport(socket) 52 | { 53 | $*socket := @cast(socket, "struct socket", "kernel") 54 | sk = $*socket->sk 55 | return __tcp_sock_dport(sk) 56 | } 57 | 58 | 59 | function socket_sport(socket) 60 | { 61 | $*socket := @cast(socket, "struct socket", "kernel") 62 | sk = $*socket->sk 63 | return __tcp_sock_sport(sk) 64 | } 65 | -------------------------------------------------------------------------------- /samples/ngx-lua-shdict-writes.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | 4 | /* 5 | * Copyright (C) Yichun Zhang (agentzh) 6 | */ 7 | 8 | 9 | @define NGX_HTTP_LUA_SHDICT_ADD %( 0x0001 %) 10 | @define NGX_HTTP_LUA_SHDICT_REPLACE %( 0x0002 %) 11 | @define NGX_HTTP_LUA_SHDICT_SAFE_STORE %( 0x0004 %) 12 | 13 | 14 | @use luajit 15 | 16 | 17 | global zone_names 18 | 19 | 20 | function handler(L, flags) 21 | { 22 | zone = luajit_touserdata(L, 1) 23 | if (zone == 0) { 24 | warn("failed to get user data") 25 | return 0 26 | } 27 | 28 | dict = zone_names[zone] 29 | if (dict == "") { 30 | //printf("generating name...\n") 31 | $*zone := @cast(zone, "ngx_shm_zone_t", "$^exec_path") 32 | ctx = $*zone->data 33 | $*ctx := @cast(ctx, "ngx_http_lua_shdict_ctx_t", "$^exec_path") 34 | //printf("zone == %p\n", zone) 35 | dict = user_string_n($*ctx->name->data, $*ctx->name->len) 36 | zone_names[zone] = dict 37 | } 38 | 39 | //printf("dict == %s\n", name) 40 | if ("$^arg_dict :default()" != "" && dict != "$^arg_dict") { 41 | return 0 42 | } 43 | 44 | if (flags & @NGX_HTTP_LUA_SHDICT_ADD) { 45 | op = "add" 46 | 47 | } else if (flags & @NGX_HTTP_LUA_SHDICT_REPLACE) { 48 | op = "replace" 49 | 50 | } else { 51 | op = "set" 52 | } 53 | 54 | if (flags & @NGX_HTTP_LUA_SHDICT_SAFE_STORE) { 55 | op = "safe_" . op 56 | } 57 | 58 | printf("[%d] %s key=%s value_len=%d dict=%s\n", gettimeofday_s(), 59 | op, luajit_tostring(L, 2), luajit_tostringlen(L, 3), dict) 60 | } 61 | 62 | 63 | probe process("$^exec_path").function("ngx_http_lua_shdict_set_helper") 64 | { 65 | handler($L, $flags) 66 | } 67 | 68 | 69 | probe begin 70 | { 71 | warn(sprintf("Tracing process %d ($^exec_path).\nHit Ctrl-C to end.\n", target())) 72 | } 73 | -------------------------------------------------------------------------------- /samples/ngx-upstream-latency.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @define EAGAIN %(11 %) 4 | 5 | global latency, waiting_resp 6 | 7 | function match_port(sk) 8 | { 9 | return __tcp_sock_dport(sk) == $^arg_port 10 | } 11 | 12 | probe tcp.sendmsg.return 13 | { 14 | if (pid() == target()) { 15 | sk = $sk 16 | if (match_port(sk)) { 17 | //printf("sendmsg return: %d\n", $return) 18 | waiting_resp[sk] = gettimeofday_us() 19 | } 20 | } 21 | } 22 | 23 | probe tcp.recvmsg.return 24 | { 25 | if (pid() == target()) { 26 | sk = $sk 27 | if (match_port(sk)) { 28 | //printf("recvmsg return: %d\n", $return) 29 | //print_ubacktrace() 30 | begin = waiting_resp[sk] 31 | if (begin) { 32 | if ($return != -@EAGAIN) { 33 | latency <<< gettimeofday_us() - begin 34 | } 35 | waiting_resp[sk] = 0 36 | } 37 | } 38 | } 39 | } 40 | 41 | probe tcp.disconnect 42 | { 43 | if (pid() == target()) { 44 | sk = $sk 45 | if (match_port(sk) && waiting_resp[sk]) { 46 | delete waiting_resp[sk] 47 | } 48 | } 49 | } 50 | 51 | probe end { 52 | printf("\n") 53 | cnt = @count(latency) 54 | if (cnt == 0) { 55 | println("No samples found so far.\n") 56 | 57 | } else { 58 | printf("Distribution of the TCP query latency (in microseconds) for %d samples.\n", 59 | cnt) 60 | printf("(min/avg/max: %d/%d/%d)\n", @min(latency), 61 | @avg(latency), @max(latency)) 62 | print(@hist_log(latency)) 63 | } 64 | } 65 | 66 | probe begin 67 | { 68 | printf("Tracing process %d ($^exec_path)\n", target()) 69 | %( "$^arg_time :default()" != "" %? 70 | printf("Please wait for $^arg_time seconds...\n") 71 | %: 72 | printf("Hit Ctrl-C to end.\n") 73 | %) 74 | } 75 | 76 | %( "$^arg_time" != "" %? 77 | probe timer.s($^arg_time) { 78 | exit() 79 | } 80 | %) 81 | -------------------------------------------------------------------------------- /samples/ngx-zlib-total-deflate-time.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global active_reqs 8 | global active_req 9 | global exec_begin 10 | global exec_elapsed 11 | global stats 12 | 13 | probe @pfunc(ngx_http_process_request) 14 | { 15 | active_reqs[$r] = 1 16 | } 17 | 18 | probe @pfunc(ngx_http_gzip_body_filter) 19 | { 20 | r = $r 21 | if (active_reqs[r]) { 22 | active_req = r 23 | 24 | } else { 25 | active_req = 0 26 | } 27 | } 28 | 29 | probe @pfunc(deflate)!, 30 | process("$^libz_path").function("deflate") 31 | { 32 | //warn("HERE") 33 | if (active_req) { 34 | exec_begin = gettimeofday_us() 35 | } 36 | } 37 | 38 | probe @pfunc(deflate).return!, 39 | process("$^libz_path").function("deflate").return 40 | { 41 | if (exec_begin) { 42 | exec_elapsed[active_req] += gettimeofday_us() - exec_begin 43 | exec_begin = 0 44 | } 45 | active_req = 0 46 | } 47 | 48 | probe @pfunc(ngx_http_free_request) 49 | { 50 | r = $r 51 | elapsed = exec_elapsed[r] 52 | if (elapsed) { 53 | stats <<< elapsed 54 | } 55 | delete exec_elapsed[r] 56 | delete active_reqs[r] 57 | exec_begin = 0 58 | } 59 | 60 | probe begin { 61 | printf("Start tracing process %d ($^exec_path)...\n", target()) 62 | %( "$^arg_time :default()" != "" %? 63 | printf("Please wait for $^arg_time seconds...\n") 64 | %: 65 | printf("Hit Ctrl-C to end.\n") 66 | %) 67 | } 68 | 69 | %( "$^arg_time" != "" %? 70 | probe timer.s($^arg_time) { 71 | exit() 72 | } 73 | %) 74 | 75 | probe end { 76 | count = @count(stats) 77 | if (count == 0) { 78 | printf("\nNo samples found so far.\n") 79 | 80 | } else { 81 | printf("\nDistribution of zlib deflate time (accumulated in each request, in microseconds) for %d samples:\n", 82 | count) 83 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 84 | @max(stats)) 85 | print(@hist_log(stats)) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /samples/ngx-lua-udp-total-recv-time.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | @use nginx.request 8 | 9 | global active_reqs 10 | global recv_begin 11 | global recv_elapsed 12 | global stats 13 | 14 | probe @pfunc(ngx_http_process_request) 15 | { 16 | r = $r 17 | %( "$^arg_uri :default()" != "" %? 18 | if (ngx_req_uri(r) == "$^arg_uri") { 19 | active_reqs[r] = 1 20 | } 21 | %: 22 | active_reqs[r] = 1 23 | %) 24 | } 25 | 26 | probe @pfunc(ngx_http_lua_socket_udp_read) 27 | { 28 | r = $r 29 | u = $u 30 | if (active_reqs[r]) { 31 | if (!recv_begin[r, u]) { 32 | recv_begin[r, u] = gettimeofday_us() 33 | } 34 | } 35 | } 36 | 37 | probe @pfunc(ngx_http_lua_socket_udp_receive_retval_handler) 38 | { 39 | r = $r 40 | u = $u 41 | begin = recv_begin[r, u] 42 | if (begin) { 43 | recv_elapsed[r] += gettimeofday_us() - begin 44 | delete recv_begin[r, u] 45 | } 46 | } 47 | 48 | probe @pfunc(ngx_http_log_request) 49 | { 50 | r = $r 51 | elapsed = recv_elapsed[r] 52 | if (elapsed) { 53 | stats <<< elapsed 54 | } 55 | delete recv_elapsed[r] 56 | delete active_reqs[r] 57 | } 58 | 59 | probe begin { 60 | printf("Start tracing process %d ($^exec_path)...\n", target()) 61 | %( "$^arg_time :default()" != "" %? 62 | printf("Please wait for $^arg_time seconds...\n") 63 | %: 64 | printf("Hit Ctrl-C to end.\n") 65 | %) 66 | } 67 | 68 | %( "$^arg_time" != "" %? 69 | probe timer.s($^arg_time) { 70 | exit() 71 | } 72 | %) 73 | 74 | probe end { 75 | count = @count(stats) 76 | if (count == 0) { 77 | printf("\nNo samples found so far.\n") 78 | 79 | } else { 80 | printf("\nDistribution of the ngx_lua ngx.socket.udp receive latencies (accumulated in each request, in microseconds) for %d samples:\n", 81 | count) 82 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 83 | @max(stats)) 84 | print(@hist_log(stats)) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /samples/ngx-pcre-dist.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @define now %( 4 | %( "$^arg_utime :default()" != "" %? 5 | cputime_to_usecs(task_utime()) 6 | %: 7 | gettimeofday_us() 8 | %) 9 | %) 10 | 11 | probe begin { 12 | printf("Start tracing %d ($^exec_path)\n", target()) 13 | %( "$^arg_time :default()" != "" %? 14 | printf("Please wait for $^arg_time seconds...\n") 15 | %: 16 | printf("Hit Ctrl-C to end.\n") 17 | %) 18 | } 19 | 20 | %( "$^arg_exec_time :default()" != "" %? 21 | global begin 22 | global exectimes 23 | 24 | probe process("$^libpcre_path").function("pcre_exec") 25 | { 26 | if (target() == pid()) { 27 | begin = @now 28 | } 29 | } 30 | 31 | probe process("$^libpcre_path").function("pcre_exec").return{ 32 | if (target() == pid() && begin) { 33 | elapsed = @now - begin 34 | exectimes <<< elapsed 35 | } 36 | } 37 | 38 | probe end 39 | { 40 | if (!begin) { 41 | println("\nNo pcre_exec() calls found so far.") 42 | 43 | } else { 44 | printf("\nLogarithmic histogram for pcre_exec running time distribution (us) for %d sample:\n", @count(exectimes)) 45 | printf("(min/avg/max: %d/%d/%d)\n", @min(exectimes), @avg(exectimes), @max(exectimes)) 46 | print(@hist_log(exectimes)) 47 | } 48 | } 49 | 50 | %: 51 | 52 | global datalens 53 | global found 54 | 55 | probe process("$^libpcre_path").function("pcre_exec") 56 | { 57 | if (target() == pid()) { 58 | found = 1 59 | datalens <<< $length 60 | //printf("len: %d, ofs: %d", $length, $start_offset) 61 | } 62 | } 63 | 64 | probe end 65 | { 66 | if (!found) { 67 | println("\nNo pcre_exec() calls found so far.") 68 | 69 | } else { 70 | printf("\nLogarithmic histogram for data length distribution (byte) for %d samples:\n", @count(datalens)) 71 | printf("(min/avg/max: %d/%d/%d)\n", @min(datalens), @avg(datalens), @max(datalens)) 72 | print(@hist_log(datalens)) 73 | } 74 | } 75 | %) 76 | 77 | %("$^arg_time" != "" %? 78 | probe timer.s($^arg_time) { 79 | exit() 80 | } 81 | %) 82 | -------------------------------------------------------------------------------- /samples/ngx-lj-trace-exits.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # check how many times LuaJIT exits a compiled trace every second. 4 | 5 | global active_req, req_exits, stats, total_nreqs, compiled_nreqs 6 | 7 | probe @pfunc(ngx_http_lua_run_thread), 8 | @pfunc(ngx_http_lua_log_handler), 9 | @pfunc(ngx_http_lua_set_by_chunk), 10 | @pfunc(ngx_http_lua_header_filter_by_chunk), 11 | @pfunc(ngx_http_lua_body_filter_by_chunk) 12 | { 13 | active_req = $r->main 14 | } 15 | 16 | probe @pfunc(ngx_http_lua_run_thread).return, 17 | @pfunc(ngx_http_lua_log_handler).return, 18 | @pfunc(ngx_http_lua_set_by_chunk).return, 19 | @pfunc(ngx_http_lua_header_filter_by_chunk).return, 20 | @pfunc(ngx_http_lua_body_filter_by_chunk).return 21 | { 22 | active_req = 0 23 | } 24 | 25 | probe @pfunc(ngx_http_free_request) 26 | { 27 | total_nreqs++ 28 | r = $r->main 29 | c = req_exits[r] 30 | if (c) { 31 | compiled_nreqs++ 32 | stats <<< c 33 | delete req_exits[r] 34 | } 35 | } 36 | 37 | probe process("$^libluajit_path").function("lj_trace_exit") { 38 | if (active_req) { 39 | req_exits[active_req]++ 40 | } 41 | } 42 | 43 | probe begin { 44 | printf("Start tracing process %d ($^exec_path)...\n", target()) 45 | %( "$^arg_time :default()" != "" %? 46 | printf("Please wait for $^arg_time seconds...\n") 47 | %: 48 | printf("Hit Ctrl-C to end.\n") 49 | %) 50 | } 51 | 52 | %( "$^arg_time" != "" %? 53 | probe timer.s($^arg_time) { 54 | exit() 55 | } 56 | %) 57 | 58 | probe end { 59 | count = @count(stats) 60 | if (count == 0) { 61 | printf("\nNo samples found so far.\n") 62 | 63 | } else { 64 | printf("\n%d out of %d requests used compiled traces generated by LuaJIT.\n", 65 | compiled_nreqs, total_nreqs) 66 | 67 | printf("Distribution of LuaJIT trace exit times per request for %d sample(s):\n", 68 | compiled_nreqs) 69 | 70 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 71 | @max(stats)) 72 | print(@hist_log(stats)) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /samples/ngx-zlib-deflate-time.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global exec_begin 8 | global stats 9 | 10 | @define process_return %( 11 | if (exec_begin) { 12 | elapsed = gettimeofday_us() - exec_begin 13 | if (elapsed > 4) { 14 | stats <<< elapsed 15 | } 16 | exec_begin = 0 17 | } 18 | %) 19 | 20 | @define process_enter %( 21 | %( $^arg_from :default(0) != 0 && $^arg_to :default(0) != 0 %? 22 | size = $strm->avail_in 23 | if (size >= $^arg_from && size <= $^arg_to) { 24 | exec_begin = gettimeofday_us() 25 | } else { 26 | exec_begin = 0 27 | } 28 | %: 29 | exec_begin = gettimeofday_us() 30 | %) 31 | %) 32 | 33 | probe @pfunc(deflate)!, 34 | process("$^libz_path").function("deflate") 35 | { 36 | //warn("HERE") 37 | @process_enter 38 | } 39 | 40 | probe @pfunc(deflateEnd)!, 41 | process("$^libz_path").function("deflateEnd") 42 | { 43 | //warn("HERE") 44 | @process_enter 45 | } 46 | 47 | 48 | probe @pfunc(deflate).return!, 49 | process("$^libz_path").function("deflate").return 50 | { 51 | @process_return 52 | } 53 | 54 | probe @pfunc(deflateEnd).return!, 55 | process("$^libz_path").function("deflateEnd").return 56 | { 57 | @process_return 58 | } 59 | 60 | probe begin { 61 | printf("Start tracing process %d ($^exec_path)...\n", target()) 62 | %( "$^arg_time :default()" != "" %? 63 | printf("Please wait for $^arg_time seconds...\n") 64 | %: 65 | printf("Hit Ctrl-C to end.\n") 66 | %) 67 | } 68 | 69 | %( "$^arg_time" != "" %? 70 | probe timer.s($^arg_time) { 71 | exit() 72 | } 73 | %) 74 | 75 | probe end { 76 | count = @count(stats) 77 | if (count == 0) { 78 | printf("\nNo samples found so far.\n") 79 | 80 | } else { 81 | printf("\nDistribution of zlib deflate time (in microseconds) for %d samples:\n", 82 | count) 83 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 84 | @max(stats)) 85 | print(@hist_log(stats)) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /samples/lj-lua-stacks.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit 5 | 6 | global bts, jitted 7 | 8 | function process_event() 9 | { 10 | if (@defined(@var("globalL", "$^exec_path"))) { 11 | mL = @var("globalL", "$^exec_path") 12 | 13 | } else { 14 | mL = ngx_lua_get_main_lua_vm() 15 | } 16 | 17 | if (mL == 0) { 18 | return 0 19 | } 20 | 21 | g = luajit_G(mL) 22 | if (g == 0) { 23 | return 0 24 | } 25 | 26 | L = luajit_cur_thread(g) 27 | if (L == 0) { 28 | return 0 29 | } 30 | 31 | vmstate = luajit_vm_state(g) 32 | if (vmstate >= 0) { 33 | /* compiled Lua code */ 34 | 35 | jitted++ 36 | 37 | %( "$^arg_nojit :default()" != "" %? 38 | return 0; 39 | %) 40 | } else { 41 | %( "$^arg_nointerp :default()" != "" %? 42 | return 0 43 | %) 44 | } 45 | 46 | bt = luajit_backtrace(L, g, $^arg_detailed :default(0) ? 0 : 1) 47 | if (bt != "") { 48 | f = probefunc() 49 | if (f != "") { 50 | bt = f . "\n" . bt 51 | } 52 | //printf("backtrace: %s\n", bt) 53 | bts[bt] <<< 1 54 | return 1 55 | } 56 | 57 | return 0 58 | } 59 | 60 | probe $^arg_probe :default(timer.profile) 61 | { 62 | if (pid() == target()) { 63 | process_event() 64 | } 65 | } 66 | 67 | function output_report() { 68 | foreach (bt in bts- limit $^arg_limit :default(1000)) { 69 | cnt = @count(bts[bt]) 70 | if (cnt <= $^arg_min :default(2)) { 71 | break 72 | } 73 | printf("%s\t%d\n", bt, cnt) 74 | } 75 | } 76 | 77 | %( "$^arg_time :default()" != "" %? 78 | probe timer.s($^arg_time) 79 | { 80 | warn("Time's up. Quitting now...\n") 81 | exit() 82 | } 83 | %) 84 | 85 | probe end { 86 | output_report() 87 | warn(sprintf("Found %d JITted samples.\n", jitted)) 88 | } 89 | 90 | probe begin 91 | { 92 | warn(sprintf("Start tracing %d ($^exec_path)\n", target())) 93 | %( "$^arg_time" != "" %? 94 | warn(sprintf("Please wait for $^arg_time seconds...\n")) 95 | %: 96 | warn("Hit Ctrl-C to end.\n") 97 | %) 98 | } 99 | -------------------------------------------------------------------------------- /samples/sample-bt-leaks-wrapalloc.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Copyright (C) Yichun Zhang (agentzh) 4 | # this requires using wrapalloc.so in the wrapalloc 5 | # project (https://github.com/agentzh/wrapalloc) 6 | # to LD_PRELOAD your application before using this tool. 7 | 8 | global ptr2bt 9 | global ptr2size 10 | global bt_stats 11 | global quit 12 | global free_misses 13 | 14 | probe begin 15 | { 16 | warn(sprintf("Start tracing %d ($^exec_path)...", target())) 17 | warn("Wait for $^arg_time :default(10) sec to complete.\n") 18 | } 19 | 20 | function handle_quit() 21 | { 22 | if (free_misses) { 23 | warn(sprintf("free misses: %d", free_misses)) 24 | } 25 | 26 | foreach (bt in bt_stats) { 27 | print_ustack(bt) 28 | printf("\t%d\n", @sum(bt_stats[bt])) 29 | } 30 | 31 | exit() 32 | } 33 | 34 | function free_ptr(ptr) 35 | { 36 | if (quit) { 37 | handle_quit() 38 | 39 | } else { 40 | bt = ptr2bt[ptr] 41 | delete ptr2bt[ptr] 42 | 43 | bytes = ptr2size[ptr] 44 | delete ptr2size[ptr] 45 | 46 | if (bt == "" && bytes == 0) { 47 | free_misses++ 48 | 49 | } else { 50 | bt_stats[bt] <<< -bytes 51 | if (@sum(bt_stats[bt]) == 0) { 52 | delete bt_stats[bt] 53 | } 54 | } 55 | } 56 | } 57 | 58 | function alloc_size(ptr, size) 59 | { 60 | if (quit) { 61 | handle_quit() 62 | 63 | } else { 64 | bt = ubacktrace() 65 | //warn(sprintf("alloc: %p (bytes %d) %s\n", ptr, size, ubacktrace())) 66 | ptr2bt[ptr] = bt 67 | ptr2size[ptr] = size 68 | bt_stats[bt] <<< size 69 | } 70 | } 71 | 72 | probe process("$^libwrapalloc_path").function("probe_alloc_event") 73 | { 74 | //warn(sprintf("probe alloc %p %d\n", $memptr, $bytes)) 75 | alloc_size($memptr, $bytes) 76 | } 77 | 78 | probe process("$^libwrapalloc_path").function("probe_free_event") 79 | { 80 | //warn(sprintf("probe free %p\n", $memptr)) 81 | free_ptr($memptr) 82 | } 83 | 84 | probe timer.s($^arg_time) 85 | { 86 | quit = 1 87 | delete ptr2bt 88 | delete ptr2size 89 | } 90 | -------------------------------------------------------------------------------- /samples/ngx-lua-slow-udp-query.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global active_reqs 8 | global recv_begin 9 | global queries 10 | global sending 11 | 12 | probe @pfunc(ngx_http_process_request) 13 | { 14 | sending = 0 15 | active_reqs[$r] = 1 16 | } 17 | 18 | probe @pfunc(ngx_http_lua_socket_udp_send) 19 | { 20 | sending = 1 21 | } 22 | 23 | probe @pfunc(ngx_unix_send) 24 | { 25 | if (sending) { 26 | //warn(sprintf("HERE: %d, %.*s", $size, $size, user_string_n_quoted($buf, $size))) 27 | sending = 0 28 | size = $size 29 | query = "" 30 | for (i = 0; i < size; i++) { 31 | if ((i + 1) % 10 == 0) { 32 | query .= sprintf(" %02x\n", $buf[i]) 33 | } else { 34 | query .= sprintf(" %02x", $buf[i]) 35 | } 36 | } 37 | queries[$c] = query 38 | } 39 | } 40 | 41 | probe @pfunc(ngx_http_lua_socket_udp_read) 42 | { 43 | r = $r 44 | u = $u 45 | sending = 0 46 | if (active_reqs[r]) { 47 | if (!recv_begin[r, u]) { 48 | recv_begin[r, u] = gettimeofday_us() 49 | } 50 | } 51 | } 52 | 53 | probe @pfunc(ngx_http_lua_socket_udp_finalize) 54 | { 55 | sending = 0 56 | delete queries[$u->udp_connection->connection] 57 | } 58 | 59 | probe @pfunc(ngx_http_log_request) 60 | { 61 | sending = 0 62 | delete active_reqs[$r] 63 | } 64 | 65 | probe @pfunc(ngx_http_lua_socket_udp_receive_retval_handler) 66 | { 67 | sending = 0 68 | r = $r 69 | u = $u 70 | begin = recv_begin[r, u] 71 | if (begin) { 72 | elapsed = gettimeofday_us() - begin 73 | if (elapsed >= $^arg_threshold :default(1000000)) { 74 | printf("u->ft_type: %d, elapsed: %dus\n", $u->ft_type, elapsed) 75 | query = queries[$u->udp_connection->connection] 76 | printf("Query bytes (hex dump):\n%s\n", query) 77 | exit() 78 | } 79 | } 80 | delete recv_begin[r, u] 81 | } 82 | 83 | probe begin { 84 | printf("Start tracing process %d ($^exec_path)...\n", target()) 85 | printf("Hit Ctrl-C to end.\n") 86 | } 87 | 88 | -------------------------------------------------------------------------------- /samples/ngx-lua-tcp-total-recv-time.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | @use nginx.request 8 | 9 | global active_reqs 10 | global recv_begin 11 | global recv_elapsed 12 | global stats 13 | 14 | probe @pfunc(ngx_http_process_request) 15 | { 16 | r = $r 17 | %( "$^arg_uri :default()" != "" %? 18 | if (ngx_req_uri(r) == "$^arg_uri") { 19 | active_reqs[r] = 1 20 | } 21 | %: 22 | active_reqs[r] = 1 23 | %) 24 | } 25 | 26 | probe @pfunc(ngx_http_lua_socket_tcp_read) 27 | { 28 | r = $r 29 | u = $u 30 | if (active_reqs[r]) { 31 | if (!recv_begin[r, u]) { 32 | recv_begin[r, u] = gettimeofday_us() 33 | } 34 | } 35 | } 36 | 37 | probe @pfunc(ngx_http_lua_socket_tcp_finalize) 38 | { 39 | delete recv_begin[$r, $u] 40 | } 41 | 42 | probe @pfunc(ngx_http_lua_socket_tcp_receive_retval_handler) 43 | { 44 | r = $r 45 | u = $u 46 | begin = recv_begin[r, u] 47 | if (begin) { 48 | recv_elapsed[r] += gettimeofday_us() - begin 49 | delete recv_begin[r, u] 50 | } 51 | } 52 | 53 | probe @pfunc(ngx_http_log_request) 54 | { 55 | r = $r 56 | elapsed = recv_elapsed[r] 57 | if (elapsed) { 58 | stats <<< elapsed 59 | } 60 | delete recv_elapsed[r] 61 | delete active_reqs[r] 62 | } 63 | 64 | probe begin { 65 | printf("Start tracing process %d ($^exec_path)...\n", target()) 66 | %( "$^arg_time :default()" != "" %? 67 | printf("Please wait for $^arg_time seconds...\n") 68 | %: 69 | printf("Hit Ctrl-C to end.\n") 70 | %) 71 | } 72 | 73 | %( "$^arg_time" != "" %? 74 | probe timer.s($^arg_time) { 75 | exit() 76 | } 77 | %) 78 | 79 | probe end { 80 | count = @count(stats) 81 | if (count == 0) { 82 | printf("\nNo samples found so far.\n") 83 | 84 | } else { 85 | printf("\nDistribution of the ngx_lua ngx.socket.tcp receive latencies (accumulated in each request, in microseconds) for %d samples:\n", 86 | count) 87 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 88 | @max(stats)) 89 | print(@hist_log(stats)) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /samples/ngx-req-latency-distr.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | global begin_times 4 | global latencies 5 | global method_id 6 | 7 | probe @pfunc(ngx_http_handler) { 8 | if (pid() == target() && $r == $r->main) { 9 | if ("$^arg_method :default()" != "") { 10 | if (method_id == 0) { 11 | if ($r->method_name->len > 0) { 12 | method = user_string_n($r->method_name->data, $r->method_name->len) 13 | //printf("found method %s\n", method) 14 | if (method == "$^arg_method") { 15 | method_id = $r->method 16 | //printf("found method id %d\n", method_id) 17 | } 18 | } 19 | } 20 | 21 | if (method_id && method_id == $r->method) { 22 | begin_times[$r] = gettimeofday_us() 23 | } 24 | 25 | } else { 26 | begin_times[$r] = gettimeofday_us() 27 | } 28 | } 29 | } 30 | 31 | probe @pfunc(ngx_http_log_request) { 32 | if (pid() == target()) { 33 | begin = begin_times[$r] 34 | if (begin) { 35 | latencies <<< gettimeofday_us() - begin 36 | delete begin_times[$r] 37 | } 38 | } 39 | } 40 | 41 | probe begin { 42 | printf("Start tracing process %d ($^exec_path)...\n", target()) 43 | %( "$^arg_time :default()" != "" %? 44 | printf("Please wait for $^arg_time seconds...\n") 45 | %: 46 | printf("Hit Ctrl-C to end.\n") 47 | %) 48 | 49 | if ("$^arg_method" != "") { 50 | printf("(Tracing only $^arg_method request methods)\n") 51 | } 52 | } 53 | 54 | %( "$^arg_time" != "" %? 55 | probe timer.s($^arg_time) { 56 | exit() 57 | } 58 | %) 59 | 60 | probe end { 61 | count = @count(latencies) 62 | if (count == 0) { 63 | printf("\nNo samples found so far.\n") 64 | 65 | } else { 66 | printf("\nDistribution of the main request latencies (in microseconds) for %d samples:\n", 67 | count) 68 | printf("(min/avg/max: %d/%d/%d)\n", @min(latencies), @avg(latencies), 69 | @max(latencies)) 70 | print(@hist_log(latencies)) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /samples/vfs-page-cache-misses.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Inspired by Josh Stone's stap script posted on the systemtap 4 | # mailing list: https://sourceware.org/ml/systemtap/2011-q2/msg00157.html 5 | 6 | global pages, active 7 | global total_paged, total_read_bytes 8 | global miss 9 | global misses 10 | 11 | probe $^arg_probe :default(vfs.read) { 12 | if (pid() == target() && (!$^arg_inode :default(0) || ino == $^arg_inode)) { 13 | tid = tid() 14 | active[tid] = 1 15 | pages[tid] = 0 16 | total_read_bytes += bytes_to_read 17 | } 18 | } 19 | 20 | probe vfs.add_to_page_cache.return { 21 | if (pid() == target()) { 22 | tid = tid() 23 | 24 | if (active[tid]) { 25 | pages[tid] += size 26 | miss[tid] = 1 27 | } 28 | } 29 | } 30 | 31 | probe $^arg_probe.return { 32 | if (pid() == target()) { 33 | tid = tid() 34 | 35 | if (active[tid]) { 36 | active[tid] = 0 37 | 38 | total_paged += pages[tid] 39 | 40 | if (miss[tid]) { 41 | misses <<< 1 42 | miss[tid] = 0 43 | 44 | } else { 45 | misses <<< 0 46 | } 47 | } 48 | } 49 | } 50 | 51 | probe timer.s($^arg_period :default(2)) { 52 | total = @count(misses) 53 | if (total == 0) { 54 | printf("No $^arg_probe observed in the last $^arg_period seconds.\n") 55 | 56 | } else { 57 | missed = @sum(misses) 58 | total_read_kb = total_read_bytes / 1024 59 | pagesize = mem_page_size() 60 | total_paged_kb = total_paged * pagesize / 1024 61 | 62 | printf("%d $^arg_probe operations, %d missed operations, operation miss rate: %d%%\n", 63 | total, missed, missed * 100 / total) 64 | 65 | printf("total read %d KB, %d pages added (page size: %dB), size miss rate: %d%%\n", 66 | total_read_kb, total_paged, pagesize, total_paged_kb * 100 / total_read_kb) 67 | 68 | delete misses 69 | delete total_paged 70 | delete total_read_bytes 71 | } 72 | } 73 | 74 | probe begin { 75 | printf("Tracing %d...\nHit Ctrl-C to end.\n\n", target()) 76 | } 77 | -------------------------------------------------------------------------------- /tapset/nginx/upstream.sxx: -------------------------------------------------------------------------------- 1 | // module nginx.upstream 2 | 3 | function ngx_u_resp_time(u) 4 | { 5 | $*u := @cast(u, "ngx_http_upstream_t", "$^exec_path") 6 | begin = $*u->state->response_sec * 1000 + $*u->state->response_msec 7 | if (begin > 1396988097414) { 8 | return gettimeofday_ms() - begin 9 | } 10 | return begin 11 | } 12 | 13 | 14 | function ngx_u_resp_statuses(r) 15 | { 16 | $*r := @cast(r, "ngx_http_request_t", "$^exec_path") 17 | len = $*r->upstream_states->nelts 18 | state = $*r->upstream_states->elts 19 | $*state := @cast(state, "ngx_http_upstream_state_t", "$^exec_path") 20 | 21 | s = "" 22 | for (i = 0; i < len; i++) { 23 | s .= sprintf(" %d", $*state[i]->status) 24 | } 25 | return s 26 | } 27 | 28 | 29 | function ngx_u_resp_lens(r) 30 | { 31 | len = $*r->upstream_states->nelts 32 | state = $*r->upstream_states->elts 33 | $*state := @cast(state, "ngx_http_upstream_state_t", "$^exec_path") 34 | 35 | s = "" 36 | for (i = 0; i < len; i++) { 37 | s .= sprintf(" %d", $*state[i]->response_length) 38 | } 39 | return s 40 | } 41 | 42 | 43 | 44 | function ngx_u_resp_times(r) 45 | { 46 | len = $*r->upstream_states->nelts 47 | state = $*r->upstream_states->elts 48 | $*state := @cast(state, "ngx_http_upstream_state_t", "$^exec_path") 49 | 50 | s = "" 51 | for (i = 0; i < len; i++) { 52 | begin = $*state[i]->response_sec * 1000 + $*state[i]->response_msec 53 | if (begin > 1396988097414) { 54 | time = gettimeofday_ms() - begin 55 | } else { 56 | time = begin 57 | } 58 | 59 | s .= sprintf(" %d", time) 60 | } 61 | 62 | return s 63 | } 64 | 65 | 66 | function ngx_u_addr(r) 67 | { 68 | len = $*r->upstream_states->nelts 69 | state = $*r->upstream_states->elts 70 | $*state := @cast(state, "ngx_http_upstream_state_t", "$^exec_path") 71 | 72 | s = "" 73 | for (i = 0; i < len; i++) { 74 | peer = $*state[i]->peer 75 | if (peer) { 76 | $*peer := @cast(peer, "ngx_str_t", "$^exec_path") 77 | len = $*peer->len 78 | if (len) { 79 | s .= sprintf(" %s", user_string_n_warn($*peer->data, len)) 80 | } 81 | } 82 | } 83 | 84 | return s 85 | } 86 | -------------------------------------------------------------------------------- /samples/ngx-req-pool-allocs.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Copyright (C) Yichun Zhang 4 | 5 | global stats, alloc, active 6 | 7 | probe @pfunc(ngx_http_free_request).callee("ngx_destroy_pool") 8 | { 9 | pool = $pool 10 | //printf("free req pool %p\n", pool) 11 | if (active[pool]) { 12 | stats <<< @count(alloc[pool]) 13 | delete alloc[pool] 14 | delete active[pool] 15 | } 16 | } 17 | 18 | probe @pfunc(ngx_http_create_request).return 19 | { 20 | r = $return 21 | $*r := @cast(r, "ngx_http_request_t") 22 | pool = $*r->pool 23 | if (pool) { 24 | //printf("create req pool %p\n", pool); 25 | active[pool] = 1 26 | alloc[pool] <<< 1 27 | } 28 | } 29 | 30 | probe @pfunc(ngx_palloc) 31 | { 32 | pool = $pool 33 | //printf("palloc %p\n", pool) 34 | $*pool := @cast(pool, "ngx_pool_t") 35 | 36 | if (active[pool]) { 37 | %( "$^arg_gross :default()" != "" %? 38 | alloc[pool] <<< 1 39 | %: 40 | sz = $size 41 | found = 0 42 | if (sz < $*pool->max) { 43 | pool = $*pool->current 44 | while (pool) { 45 | /* we omit ptr alignment here */ 46 | if ($*pool->d->end - $*pool->d->last >= sz) { 47 | found = 1 48 | break 49 | } 50 | 51 | pool = $*pool->d->next 52 | } 53 | } else { 54 | //printf("large block: %d\n", sz) 55 | } 56 | 57 | //printf("alloc: sz:%d, found:%d\n", sz, found) 58 | 59 | if (!found) { 60 | //printf("HIT\n") 61 | alloc[pool] <<< 1 62 | } 63 | %) 64 | } 65 | } 66 | 67 | probe end { 68 | count = @count(stats) 69 | if (count == 0) { 70 | printf("\nNo samples found so far.\n") 71 | 72 | } else { 73 | %( "$^arg_gross :default()" != "" %? 74 | tag = "gross" 75 | %: 76 | tag = "true" 77 | %) 78 | 79 | printf("\nDistribution of number of %s allocations in nginx request pool for %d req samples:\n", 80 | tag, count) 81 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 82 | @max(stats)) 83 | print(@hist_log(stats)) 84 | } 85 | } 86 | 87 | probe begin { 88 | printf("Start tracing process %d ($^exec_path)...\n", target()) 89 | %( "$^arg_time :default()" != "" %? 90 | printf("Please wait for $^arg_time seconds...\n") 91 | %: 92 | printf("Hit Ctrl-C to end.\n") 93 | %) 94 | } 95 | 96 | %( "$^arg_time" != "" %? 97 | probe timer.s($^arg_time) { 98 | exit() 99 | } 100 | %) 101 | -------------------------------------------------------------------------------- /samples/ngx-lua-exec-time.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global active_reqs 8 | global exec_begin 9 | global exec_elapsed 10 | global stats 11 | global filter_begin 12 | 13 | probe @pfunc(ngx_http_lua_ssl_cert_by_chunk)?, 14 | @pfunc(ngx_http_process_request) 15 | { 16 | active_reqs[$r] = 1 17 | } 18 | 19 | probe @pfunc(ngx_http_lua_run_thread), 20 | @pfunc(ngx_http_lua_log_handler), 21 | @pfunc(ngx_http_lua_set_by_chunk), 22 | @pfunc(ngx_http_lua_header_filter_by_chunk), 23 | @pfunc(ngx_http_lua_body_filter_by_chunk) 24 | { 25 | if (active_reqs[$r]) { 26 | exec_begin = gettimeofday_us() 27 | } 28 | } 29 | 30 | probe @pfunc(ngx_output_chain) 31 | { 32 | if (exec_begin) { 33 | filter_begin = gettimeofday_us() 34 | } 35 | } 36 | 37 | probe @pfunc(ngx_output_chain).return 38 | { 39 | if (exec_begin && filter_begin) { 40 | exec_begin += gettimeofday_us() - filter_begin 41 | //printf("filter runs in %dus\n", gettimeofday_us() - filter_begin) 42 | } 43 | } 44 | 45 | probe @pfunc(ngx_http_lua_run_thread).return, 46 | @pfunc(ngx_http_lua_log_handler).return, 47 | @pfunc(ngx_http_lua_set_by_chunk).return, 48 | @pfunc(ngx_http_lua_header_filter_by_chunk).return, 49 | @pfunc(ngx_http_lua_body_filter_by_chunk).return 50 | { 51 | if (exec_begin) { 52 | exec_elapsed[$r] += gettimeofday_us() - exec_begin 53 | exec_begin = 0 54 | } 55 | } 56 | 57 | probe @pfunc(ngx_http_free_request), 58 | @pfunc(ngx_http_lua_free_fake_request) 59 | { 60 | r = $r 61 | elapsed = exec_elapsed[r] 62 | if (elapsed) { 63 | %( "$^arg_max :default()" != "" %? 64 | if (elapsed <= $^arg_max) { 65 | stats <<< elapsed 66 | } 67 | %: 68 | stats <<< elapsed 69 | %) 70 | } 71 | delete exec_elapsed[r] 72 | delete active_reqs[r] 73 | exec_begin = 0 74 | } 75 | 76 | probe begin { 77 | printf("Start tracing process %d ($^exec_path)...\n", target()) 78 | %( "$^arg_time :default()" != "" %? 79 | printf("Please wait for $^arg_time seconds...\n") 80 | %: 81 | printf("Hit Ctrl-C to end.\n") 82 | %) 83 | } 84 | 85 | %( "$^arg_time" != "" %? 86 | probe timer.s($^arg_time) { 87 | exit() 88 | } 89 | %) 90 | 91 | probe end { 92 | count = @count(stats) 93 | if (count == 0) { 94 | printf("\nNo samples found so far.\n") 95 | 96 | } else { 97 | printf("\nDistribution of Lua code pure execution time (accumulated in each request, in microseconds) for %d samples:\n", 98 | count) 99 | printf("(min/avg/max: %d/%d/%d)\n", @min(stats), @avg(stats), 100 | @max(stats)) 101 | print(@hist_log(stats)) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /samples/ngx-rewrite-latency-distr.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | global entered_rewrite_phase, rewrite_phase_time, seen_find_config 8 | global latency_stats 9 | 10 | @define eval_rewrite_phase_time %( 11 | if (entered_rewrite_phase[r]) { 12 | rewrite_phase_time[r] += now - entered_rewrite_phase[r] 13 | entered_rewrite_phase[r] = 0 14 | } 15 | %) 16 | 17 | probe @pfunc(ngx_http_core_find_config_phase) 18 | { 19 | r = $r 20 | seen_find_config[r] = 1 21 | /* for internal redirects: */ 22 | entered_rewrite_phase[r] = 0 23 | } 24 | 25 | probe @pfunc(ngx_http_core_rewrite_phase) 26 | { 27 | r = $r 28 | if (seen_find_config[r] && !entered_rewrite_phase[r]) { 29 | entered_rewrite_phase[r] = gettimeofday_us() 30 | } 31 | } 32 | 33 | probe @pfunc(ngx_http_core_generic_phase) 34 | { 35 | r = $r 36 | if (seen_find_config[r]) { 37 | /* pre-access phase */ 38 | now = gettimeofday_us() 39 | @eval_rewrite_phase_time 40 | } 41 | } 42 | 43 | probe @pfunc(ngx_http_core_access_phase) 44 | { 45 | now = gettimeofday_us() 46 | r = $r 47 | @eval_rewrite_phase_time 48 | } 49 | 50 | probe @pfunc(ngx_http_core_try_files_phase) 51 | { 52 | now = gettimeofday_us() 53 | r = $r 54 | @eval_rewrite_phase_time 55 | } 56 | 57 | probe @pfunc(ngx_http_core_content_phase) 58 | { 59 | now = gettimeofday_us() 60 | r = $r 61 | @eval_rewrite_phase_time 62 | } 63 | 64 | probe @pfunc(ngx_http_log_request) 65 | { 66 | now = gettimeofday_us() 67 | r = $r 68 | @eval_rewrite_phase_time 69 | 70 | latency_stats <<< rewrite_phase_time[r] 71 | delete rewrite_phase_time[r] 72 | delete entered_rewrite_phase[r] 73 | delete seen_find_config[r] 74 | } 75 | 76 | probe end { 77 | count = @count(latency_stats) 78 | if (count == 0) { 79 | printf("No samples observed so far.\n") 80 | 81 | } else { 82 | printf("\nDistribution of the rewrite phase latencies (in microseconds) for %d samples:\n", 83 | count) 84 | printf("(min/avg/max: %d/%d/%d)\n", @min(latency_stats), 85 | @avg(latency_stats), @max(latency_stats)) 86 | print(@hist_log(latency_stats)) 87 | } 88 | } 89 | 90 | %( "$^arg_time :default()" != "" %? 91 | probe timer.s($^arg_time) { 92 | exit() 93 | } 94 | %) 95 | 96 | /* 97 | probe @pfunc(ngx_http_log_request).return 98 | { 99 | if (pid() == matched_pid && $r == matched_r) { 100 | now = gettimeofday_us() 101 | log_phase_time = now - entered_log_phase 102 | entered_log_phase = 0 103 | printf("log: %dus\n", log_phase_time) 104 | exit() 105 | } 106 | } 107 | */ 108 | 109 | probe begin { 110 | printf("Start tracing process $^target ($^exec_path)...\n") 111 | %( "$^arg_time" != "" %? 112 | printf("Please wait for $^arg_time seconds...\n") 113 | %: 114 | printf("Hit Ctrl-C to end.\n") 115 | %) 116 | } 117 | -------------------------------------------------------------------------------- /samples/sample-bt-leaks.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | # Copyright (C) Yichun Zhang (agentzh) 4 | 5 | global ptr2bt 6 | global ptr2size 7 | global bt_stats 8 | global quit 9 | global free_misses 10 | 11 | probe begin 12 | { 13 | warn(sprintf("Start tracing %d ($^exec_path)...", target())) 14 | warn("Wait for $^arg_time :default(10) sec to complete.\n") 15 | } 16 | 17 | probe process("$^libc_path").function("malloc").return, 18 | process("$^libc_path").function("calloc").return, 19 | process("$^libc_path").function("realloc").return 20 | { 21 | if (pid() == target()) { 22 | //printf("Hit %s\n", probefunc()) 23 | if (quit) { 24 | if (free_misses) { 25 | warn(sprintf("free misses: %d", free_misses)) 26 | } 27 | 28 | foreach (bt in bt_stats) { 29 | print_ustack(bt) 30 | printf("\t%d\n", @sum(bt_stats[bt])) 31 | } 32 | 33 | exit() 34 | 35 | } else { 36 | if (@defined($oldmem)) { 37 | //printf("free %p in realloc\n", $oldmem) 38 | ptr = $oldmem 39 | 40 | bt = ptr2bt[ptr] 41 | delete ptr2bt[ptr] 42 | 43 | bytes = ptr2size[ptr] 44 | delete ptr2size[ptr] 45 | 46 | bt_stats[bt] <<< -bytes 47 | if (@sum(bt_stats[bt]) == 0) { 48 | delete bt_stats[bt] 49 | } 50 | } 51 | 52 | ptr = returnval() 53 | if (ptr) { 54 | // here we use a hack specific to glibc's implementation 55 | // to get the size of the memory block because we 56 | // may not get the value of the $bytes parameter reliably. 57 | $*sizeof_size_t := &@cast(0, "size_t")[1] 58 | p = ptr - $*sizeof_size_t 59 | size = @cast(p, "size_t")[0] & ~($*sizeof_size_t - 1) 60 | 61 | //printf("alloc: %p (bytes %d)\n", ptr, size) 62 | bt = ubacktrace() 63 | ptr2bt[ptr] = bt 64 | ptr2size[ptr] = size 65 | bt_stats[bt] <<< size 66 | 67 | } else { 68 | warn("NULL returned") 69 | } 70 | } 71 | } 72 | } 73 | 74 | probe process("$^libc_path").function("free") 75 | { 76 | ptr = pointer_arg(1) 77 | if (pid() == target() && ptr) { 78 | //printf("free: %p\n", ptr) 79 | 80 | bt = ptr2bt[ptr] 81 | delete ptr2bt[ptr] 82 | 83 | bytes = ptr2size[ptr] 84 | delete ptr2size[ptr] 85 | 86 | if (bt == "" && bytes == 0) { 87 | free_misses++ 88 | 89 | } else { 90 | bt_stats[bt] <<< -bytes 91 | if (@sum(bt_stats[bt]) == 0) { 92 | delete bt_stats[bt] 93 | } 94 | } 95 | } 96 | } 97 | 98 | probe timer.s($^arg_time) 99 | { 100 | quit = 1 101 | delete ptr2bt 102 | delete ptr2size 103 | } 104 | -------------------------------------------------------------------------------- /samples/ngx-timeout-settings.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | global waiting_timer 4 | global timer_type // 0: read, 1: send, 2: connect 5 | global read_timeouts, send_timeouts, connect_timeouts 6 | 7 | @define READ_EV %( 0 %) 8 | @define SEND_EV %( 1 %) 9 | @define CONNECT_EV %( 2 %) 10 | 11 | @define EAGAIN %(11 %) 12 | @define EINPROGRESS %(115 %) 13 | 14 | @define MSG_PEEK %( 2 %) 15 | 16 | @use kernel.socket 17 | 18 | function match_port(sk) 19 | { 20 | %( $^arg_dport :default(0) != 0 %? 21 | return __tcp_sock_dport(sk) == $^arg_dport 22 | %: 23 | return __tcp_sock_sport(sk) == $^arg_sport :default(0) 24 | %) 25 | } 26 | 27 | probe tcp.recvmsg.return 28 | { 29 | if (pid() == target()) { 30 | //printf("recvmsg return: %d\n", $return) 31 | //print_ubacktrace() 32 | if ($return == -@EAGAIN && !($flags & @MSG_PEEK) && match_port($sk)) { 33 | //println("recvmsg EAGAIN") 34 | waiting_timer = 1 35 | timer_type = @READ_EV 36 | 37 | } else { 38 | waiting_timer = 0 39 | } 40 | } 41 | } 42 | 43 | probe tcp.sendmsg.return 44 | { 45 | if (pid() == target()) { 46 | //printf("sendmsg return: %d\n", $return) 47 | if ($return == -@EAGAIN && match_port($sk)) { 48 | //println("sendmsg EAGAIN") 49 | waiting_timer = 1 50 | timer_type = @SEND_EV 51 | 52 | } else { 53 | waiting_timer = 0 54 | } 55 | } 56 | } 57 | 58 | probe kernel.function("inet_stream_connect").return 59 | { 60 | if (pid() == target()) { 61 | //printf("connect return: %d\n", $return) 62 | if ($return == -@EINPROGRESS && match_port($sock->sk)) { 63 | //println("connect EINPROGRESS") 64 | waiting_timer = 1 65 | timer_type = @CONNECT_EV 66 | 67 | } else { 68 | waiting_timer = 0 69 | } 70 | } 71 | } 72 | 73 | probe @pfunc(ngx_event_add_timer) 74 | { 75 | if (pid() == target() && waiting_timer) { 76 | waiting_timer = 0 77 | if (@defined($timer)) { 78 | if (timer_type == @SEND_EV && $ev->write) { 79 | send_timeouts[$timer] <<< 1 80 | 81 | } else if (timer_type == @CONNECT_EV && $ev->write) { 82 | connect_timeouts[$timer] <<< 1 83 | 84 | } else if (timer_type == @READ_EV && !$ev->write) { 85 | read_timeouts[$timer] <<< 1 86 | 87 | } 88 | } 89 | } 90 | } 91 | 92 | probe end { 93 | printf("\n") 94 | 95 | foreach (timer in connect_timeouts- limit $^arg_limit :default(10)) { 96 | printf("Connecting timeout %dms: %d samples\n", timer, @count(connect_timeouts[timer])) 97 | } 98 | 99 | foreach (timer in send_timeouts- limit $^arg_limit) { 100 | printf("Sending timeout %dms: %d samples\n", timer, @count(send_timeouts[timer])) 101 | } 102 | 103 | foreach (timer in read_timeouts- limit $^arg_limit) { 104 | printf("Reading timeout %dms: %d samples\n", timer, @count(read_timeouts[timer])) 105 | } 106 | } 107 | 108 | probe begin 109 | { 110 | %( $^arg_dport == 0 && $^arg_sport == 0 %? 111 | error("Neither --arg dport=PORT nor --arg sport=PORT is specified.") 112 | %) 113 | warn(sprintf("Tracing process %d ($^exec_path)\n", target())) 114 | } 115 | -------------------------------------------------------------------------------- /samples/lj-str-tab.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit 5 | 6 | @define TSTR %( 4 %) 7 | 8 | global bucket_lens, bucket_lens2, bucket_len_squared, str_lens 9 | 10 | probe @pfunc(ngx_process_events_and_timers)?, timer.profile 11 | { 12 | if (pid() == target()) { 13 | if (@defined(@var("globalL", "$^exec_path"))) { 14 | L = @var("globalL", "$^exec_path") 15 | 16 | } else { 17 | L = ngx_lua_get_main_lua_vm() 18 | } 19 | 20 | if (L == 0) { 21 | printf("Failed to get the main Lua VM\n") 22 | exit() 23 | } 24 | 25 | process(L) 26 | } 27 | } 28 | 29 | function process(L) 30 | { 31 | begin = gettimeofday_us() 32 | 33 | g = luajit_G(L) 34 | $*g := @cast(g, "global_State", "$^libluajit_path") 35 | 36 | strmask = $*g->strmask 37 | strnum = $*g->strnum 38 | 39 | printf("string count: %d\n", strnum) 40 | printf("bucket count: %d\n", strmask + 1) 41 | printf("load factor: %d%%\n\n", strnum * 100/(strmask + 1)) 42 | 43 | strhash = $*g->strhash 44 | $*strhash := @cast(strhash, "GCRef", "$^libluajit_path") 45 | 46 | n = 0 47 | done = 0 48 | for (i = 0; i <= strmask; i++) { 49 | p = &$*strhash[i] 50 | 51 | depth = 0 52 | while (p) { 53 | o = luajit_gcref(p) 54 | if (o == 0) { 55 | break; 56 | } 57 | 58 | $*o := @cast(o, "GCobj", "$^libluajit_path") 59 | gct = $*o->gch->gct 60 | if (gct != @TSTR) { 61 | error(sprintf("Bad string type: %d", gct)) 62 | } 63 | size = luajit_objlen(o, @TSTR, g) 64 | str_lens <<< size 65 | 66 | depth++ 67 | 68 | if (++n == strnum) { 69 | done = 1 70 | break 71 | } 72 | 73 | p = &$*o->gch->nextgc 74 | } 75 | 76 | bucket_lens <<< depth 77 | 78 | if (depth > 0) { 79 | bucket_lens2 <<< depth 80 | bucket_len_squared <<< depth * depth 81 | } 82 | 83 | if (done) { 84 | break 85 | } 86 | } 87 | 88 | if (@count(str_lens) != strnum) { 89 | warn("string count out of sync") 90 | } 91 | 92 | printf("%d strings: max=%d, avg=%d, min=%d, sum=%d (in bytes)\n", 93 | @count(str_lens), @max(str_lens), 94 | @avg(str_lens), @min(str_lens), @sum(str_lens)) 95 | print(@hist_log(str_lens)) 96 | 97 | printf("%d buckets: max=%d, avg=%d, min=%d, sum=%d (in entries)\n", 98 | @count(bucket_lens), @max(bucket_lens), 99 | @avg(bucket_lens), @min(bucket_lens), @sum(bucket_lens)) 100 | print(@hist_log(bucket_lens)) 101 | 102 | avg = @avg(bucket_lens2) 103 | printf("%d non-empty buckets: max=%d, avg=%d, min=%d, sum=%d, var=%d (in entries)\n", 104 | @count(bucket_lens2), @max(bucket_lens2), 105 | avg, @min(bucket_lens2), @sum(bucket_lens2), 106 | @avg(bucket_len_squared) - avg * avg) 107 | print(@hist_linear(bucket_lens2, 1, 10, 1)) 108 | 109 | printf("%d microseconds elapsed in the probe handler.\n", 110 | gettimeofday_us() - begin) 111 | exit() 112 | } 113 | 114 | probe begin { 115 | printf("Start tracing %d ($^exec_path)\n\n", target()) 116 | } 117 | -------------------------------------------------------------------------------- /samples/lj-vm-states.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit 5 | 6 | global states, total, ignored 7 | 8 | function process_timer_event() 9 | { 10 | if (@defined(@var("globalL", "$^exec_path"))) { 11 | mL = @var("globalL", "$^exec_path") 12 | 13 | } else { 14 | mL = ngx_lua_get_main_lua_vm() 15 | } 16 | 17 | if (mL == 0) { 18 | ignored++ 19 | return 0 20 | } 21 | 22 | g = luajit_G(mL) 23 | if (g == 0) { 24 | ignored++ 25 | return 0 26 | } 27 | 28 | $*g := @cast(g, "global_State", "$^libluajit_path") 29 | 30 | gco = $*g->cur_L->gcptr32 31 | $*gco := @cast(gco, "GCobj", "$^libluajit_path") 32 | 33 | L = &$*gco->th 34 | $*L := @cast(L, "lua_State", "$^libluajit_path") 35 | 36 | if (L == 0) { 37 | ignored++ 38 | return 0 39 | } 40 | 41 | /* 42 | printf("cur L: %p\n", L) 43 | printf("vmstate: %d\n", $*g->vmstate) 44 | printf("cframe: %p\n", $*L->cframe) 45 | */ 46 | 47 | vmstate = $*g->vmstate 48 | if (vmstate >= 0) { 49 | /* compiled Lua code */ 50 | states["N"]++ 51 | total++ 52 | 53 | } else { 54 | if (vmstate == -1 && !$*L->cframe) { 55 | ignored++ 56 | 57 | } else { 58 | if (vmstate == -1) { 59 | states["I"]++ 60 | 61 | } else if (vmstate == -2) { 62 | states["C"]++ 63 | 64 | } else if (vmstate == -3) { 65 | if ($*g->jit_base->ptr32) { 66 | states["NG"]++ 67 | 68 | } else { 69 | states["G"]++ 70 | } 71 | 72 | } else if (vmstate == -4) { 73 | states["E"]++ 74 | 75 | } else { 76 | states["J"]++ 77 | } 78 | 79 | total++ 80 | } 81 | } 82 | 83 | return 1 84 | } 85 | 86 | probe timer.profile 87 | { 88 | if (pid() == target()) { 89 | process_timer_event() 90 | } 91 | } 92 | 93 | function gen_report() 94 | { 95 | printf("\nObserved %d Lua-running samples and ignored %d unrelated samples.\n", 96 | total, ignored) 97 | 98 | foreach (state in states-) { 99 | if (state == "I") { 100 | ctx = "Interpreted" 101 | 102 | } else if (state == "N") { 103 | ctx = "Compiled" 104 | 105 | } else if (state == "J") { 106 | ctx = "JIT Compiler" 107 | 108 | } else if (state == "C") { 109 | ctx = "C Code (by interpreted Lua)" 110 | 111 | } else if (state == "E") { 112 | ctx = "Trace exiting" 113 | 114 | } else if (state == "NG") { 115 | ctx = "Garbage Collector (compiled)" 116 | 117 | } else { 118 | /* state == "G" */ 119 | ctx = "Garbage Collector (not compiled)" 120 | } 121 | 122 | count = states[state] 123 | printf("%s: %d%% (%d samples)\n", ctx, count*100/total, count) 124 | } 125 | } 126 | 127 | probe end 128 | { 129 | if (total == 0) { 130 | printf("\nNo Lua-running samples observed.\n") 131 | 132 | } else { 133 | gen_report() 134 | } 135 | } 136 | 137 | probe begin 138 | { 139 | printf("Start tracing %d ($^exec_path)\n", target()) 140 | %( "$^arg_time :default()" != "" %? 141 | printf("Please wait for $^arg_time seconds...\n") 142 | %: 143 | printf("Hit Ctrl-C to end.\n") 144 | %) 145 | } 146 | 147 | %( "$^arg_time" != "" %? 148 | probe timer.s($^arg_time) { 149 | exit() 150 | } 151 | %) 152 | -------------------------------------------------------------------------------- /samples/luajit21-gc64/lj-vm-states.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit_gc64 5 | 6 | global states, total, ignored 7 | 8 | function process_timer_event() 9 | { 10 | if (@defined(@var("globalL", "$^exec_path"))) { 11 | mL = @var("globalL", "$^exec_path") 12 | 13 | } else { 14 | mL = ngx_lua_get_main_lua_vm() 15 | } 16 | 17 | if (mL == 0) { 18 | ignored++ 19 | return 0 20 | } 21 | 22 | g = luajit_G(mL) 23 | if (g == 0) { 24 | ignored++ 25 | return 0 26 | } 27 | 28 | $*g := @cast(g, "global_State", "$^libluajit_path") 29 | 30 | gco = $*g->cur_L->gcptr64 31 | $*gco := @cast(gco, "GCobj", "$^libluajit_path") 32 | 33 | L = &$*gco->th 34 | $*L := @cast(L, "lua_State", "$^libluajit_path") 35 | 36 | if (L == 0) { 37 | ignored++ 38 | return 0 39 | } 40 | 41 | /* 42 | printf("cur L: %p\n", L) 43 | printf("vmstate: %d\n", $*g->vmstate) 44 | printf("cframe: %p\n", $*L->cframe) 45 | */ 46 | 47 | vmstate = $*g->vmstate 48 | if (vmstate >= 0) { 49 | /* compiled Lua code */ 50 | states["N"]++ 51 | total++ 52 | 53 | } else { 54 | if (vmstate == -1 && !$*L->cframe) { 55 | ignored++ 56 | 57 | } else { 58 | if (vmstate == -1) { 59 | states["I"]++ 60 | 61 | } else if (vmstate == -2) { 62 | states["C"]++ 63 | 64 | } else if (vmstate == -3) { 65 | if ($*g->jit_base->ptr64) { 66 | states["NG"]++ 67 | 68 | } else { 69 | states["G"]++ 70 | } 71 | 72 | } else if (vmstate == -4) { 73 | states["E"]++ 74 | 75 | } else { 76 | states["J"]++ 77 | } 78 | 79 | total++ 80 | } 81 | } 82 | 83 | return 1 84 | } 85 | 86 | probe timer.profile 87 | { 88 | if (pid() == target()) { 89 | process_timer_event() 90 | } 91 | } 92 | 93 | function gen_report() 94 | { 95 | printf("\nObserved %d Lua-running samples and ignored %d unrelated samples.\n", 96 | total, ignored) 97 | 98 | foreach (state in states-) { 99 | if (state == "I") { 100 | ctx = "Interpreted" 101 | 102 | } else if (state == "N") { 103 | ctx = "Compiled" 104 | 105 | } else if (state == "J") { 106 | ctx = "JIT Compiler" 107 | 108 | } else if (state == "C") { 109 | ctx = "C Code (by interpreted Lua)" 110 | 111 | } else if (state == "E") { 112 | ctx = "Trace exiting" 113 | 114 | } else if (state == "NG") { 115 | ctx = "Garbage Collector (compiled)" 116 | 117 | } else { 118 | /* state == "G" */ 119 | ctx = "Garbage Collector (not compiled)" 120 | } 121 | 122 | count = states[state] 123 | printf("%s: %d%% (%d samples)\n", ctx, count*100/total, count) 124 | } 125 | } 126 | 127 | probe end 128 | { 129 | if (total == 0) { 130 | printf("\nNo Lua-running samples observed.\n") 131 | 132 | } else { 133 | gen_report() 134 | } 135 | } 136 | 137 | probe begin 138 | { 139 | printf("Start tracing %d ($^exec_path)\n", target()) 140 | %( "$^arg_time :default()" != "" %? 141 | printf("Please wait for $^arg_time seconds...\n") 142 | %: 143 | printf("Hit Ctrl-C to end.\n") 144 | %) 145 | } 146 | 147 | %( "$^arg_time" != "" %? 148 | probe timer.s($^arg_time) { 149 | exit() 150 | } 151 | %) 152 | -------------------------------------------------------------------------------- /samples/ngx-conn-timers.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use kernel.socket 4 | 5 | global read_timers, write_timers 6 | 7 | 8 | function get_addr_text(c, fd) 9 | { 10 | $*c := @cast(c, "ngx_connection_t", "$^exec_path") 11 | socket = sockfd_lookup(fd) 12 | if ($*c->listening) { 13 | return sprintf("src %d", socket_sport(socket)) 14 | 15 | } else { 16 | return sprintf("dst %d", socket_dport(socket)) 17 | } 18 | } 19 | 20 | 21 | probe timer.profile, @pfunc(ngx_process_events_and_timers) 22 | { 23 | if (pid() == target()) { 24 | $*cycle := @var("ngx_cycle@ngx_cycle.c", "$^exec_path") 25 | 26 | conns = $*cycle->connections 27 | $*conns := @cast(conns, "ngx_connection_t", "$^exec_path") 28 | if (!conns) { 29 | error("No connections found.\n") 30 | } 31 | 32 | conn_n = $*cycle->connection_n 33 | 34 | for (i = 0; i < conn_n; i++) { 35 | c = &$*conns[i] 36 | fd = $*c->fd 37 | now = 0 38 | addr_text = "" 39 | if (fd > 0 && !$*c->destroyed) { 40 | read = $*c->read 41 | $*read := @cast(read, "ngx_event_t", "$^exec_path") 42 | 43 | if (read && $*read->active && !$*read->eof && !$*read->timedout) { 44 | if ($*read->timer_set) { 45 | now = gettimeofday_ms() 46 | addr_text = get_addr_text(c, fd) 47 | read_timers[addr_text] <<< ($*read->timer->key - now); 48 | } 49 | } 50 | 51 | write = $*c->write 52 | $*write := @cast(write, "ngx_event_t", "$^exec_path") 53 | 54 | if (write && $*write->active && !$*write->eof && !$*write->timedout) { 55 | if ($*write->timer_set) { 56 | if (!now) { 57 | now = gettimeofday_ms() 58 | } 59 | if (addr_text == "" && $*c->sockaddr) { 60 | addr_text = get_addr_text(c, fd) 61 | } 62 | write_timers[addr_text] <<< ($*write->timer->key - now); 63 | } 64 | } 65 | } 66 | } 67 | 68 | foreach (addr_text in read_timers- limit 10) { 69 | printf("Port %s\n", addr_text) 70 | printf("================\n") 71 | 72 | cnt = @count(read_timers[addr_text]) 73 | printf("reading connections: %d\n", cnt) 74 | printf("remaining time (before timeout) distribution (in milliseconds):\n") 75 | printf("(min/avg/max: %d/%d/%d)\n", @min(read_timers[addr_text]), 76 | @avg(read_timers[addr_text]), @max(read_timers[addr_text])) 77 | print(@hist_log(read_timers[addr_text])) 78 | } 79 | 80 | foreach (addr_text in write_timers- limit 10) { 81 | printf("Port %s\n", addr_text) 82 | printf("================\n") 83 | 84 | cnt = @count(write_timers[addr_text]) 85 | printf("writing connections: %d\n", cnt) 86 | printf("Remaining time (before timeout) distribution (in milliseconds):\n") 87 | printf("(min/avg/max: %d/%d/%d)\n", @min(write_timers[addr_text]), 88 | @avg(write_timers[addr_text]), 89 | @max(write_timers[addr_text])) 90 | 91 | print(@hist_log(write_timers[addr_text])) 92 | } 93 | 94 | exit() 95 | } 96 | } 97 | 98 | 99 | probe begin { 100 | printf("Tracing %d ($^exec_path)\n\n", target()) 101 | } 102 | -------------------------------------------------------------------------------- /tapset/nginx/request.sxx: -------------------------------------------------------------------------------- 1 | // module nginx.request 2 | 3 | @define NGX_HTTP_GET %( 0x0002 %) 4 | @define NGX_HTTP_HEAD %( 0x0004 %) 5 | @define NGX_HTTP_POST %( 0x0008 %) 6 | @define NGX_HTTP_PUT %( 0x0010 %) 7 | @define NGX_HTTP_DELETE %( 0x0020 %) 8 | @define NGX_HTTP_MKCOL %( 0x0040 %) 9 | @define NGX_HTTP_COPY %( 0x0080 %) 10 | @define NGX_HTTP_MOVE %( 0x0100 %) 11 | @define NGX_HTTP_OPTIONS %( 0x0200 %) 12 | 13 | @define AF_UNIX %( 1 %) 14 | @define AF_INET6 %( 10 %) 15 | 16 | function ngx_req_method(r) 17 | { 18 | $*r := @cast(r, "ngx_http_request_t", "$^exec_path") 19 | m = $*r->method 20 | if (m == @NGX_HTTP_GET) { 21 | return "GET" 22 | } 23 | 24 | if (m == @NGX_HTTP_HEAD) { 25 | return "HEAD" 26 | } 27 | 28 | if (m == @NGX_HTTP_POST) { 29 | return "POST" 30 | } 31 | 32 | if (m == @NGX_HTTP_PUT) { 33 | return "PUT" 34 | } 35 | 36 | if (m == @NGX_HTTP_DELETE) { 37 | return "DELETE" 38 | } 39 | 40 | if (m == @NGX_HTTP_MKCOL) { 41 | return "MKCOL" 42 | } 43 | 44 | if (m == @NGX_HTTP_COPY) { 45 | return "COPY" 46 | } 47 | 48 | if (m == @NGX_HTTP_MOVE) { 49 | return "MOVE" 50 | } 51 | 52 | if (m == @NGX_HTTP_OPTIONS) { 53 | return "OPTIONS" 54 | } 55 | 56 | len = $*r->method_name->len 57 | if (len == 0) { 58 | return "" 59 | } 60 | return user_string_n_warn($*r->method_name->data, len) 61 | } 62 | 63 | 64 | function ngx_req_uri(r) 65 | { 66 | $*r := @cast(r, "ngx_http_request_t", "$^exec_path") 67 | len = $*r->uri->len 68 | if (len == 0) { 69 | return "" 70 | } 71 | return user_string_n_warn($*r->uri->data, len) 72 | } 73 | 74 | 75 | function ngx_req_args(r) 76 | { 77 | $*r := @cast(r, "ngx_http_request_t", "$^exec_path") 78 | len = $*r->args->len 79 | if (len == 0) { 80 | return "" 81 | } 82 | return user_string_n_warn($*r->args->data, len) 83 | } 84 | 85 | 86 | function ngx_req_host(r) 87 | { 88 | $*r := @cast(r, "ngx_http_request_t", "$^exec_path") 89 | host = $*r->headers_in->host 90 | if (host) { 91 | $*host := @cast(host, "ngx_table_elt_t", "$^exec_path") 92 | len = $*host->value->len 93 | if (len == 0) { 94 | return "" 95 | } 96 | return user_string_n_warn($*host->value->data, len) 97 | } 98 | return "" 99 | } 100 | 101 | 102 | function ngx_req_ua(r) 103 | { 104 | $*r := @cast(r, "ngx_http_request_t", "$^exec_path") 105 | ua = $*r->headers_in->user_agent 106 | if (ua) { 107 | $*ua := @cast(ua, "ngx_table_elt_t", "$^exec_path") 108 | len = $*ua->value->len 109 | if (len == 0) { 110 | return "" 111 | } 112 | return user_string_n_warn($*ua->value->data, len) 113 | } 114 | return "" 115 | } 116 | 117 | 118 | function ngx_req_remote_addr(r) 119 | { 120 | $*r := @cast(r, "ngx_http_request_t", "$^exec_path") 121 | len = $*r->connection->addr_text->len 122 | if (len == 0) { 123 | return "" 124 | } 125 | return user_string_n_warn($*r->connection->addr_text->data, len) 126 | } 127 | 128 | 129 | function ngx_req_remote_port(r) 130 | { 131 | $*r := @cast(r, "ngx_http_request_t", "$^exec_path") 132 | sockaddr = $*r->connection->sockaddr 133 | sa_family = $*r->connection->sockaddr->sa_family 134 | if (sa_family == @AF_INET6) { 135 | $*sockaddr_in6 := @cast(sockaddr, "sockaddr_in6") 136 | return ntohs($*sockaddr_in6->sin6_port) 137 | // return 0 138 | } 139 | 140 | if (sa_family == @AF_UNIX) { 141 | return 0 142 | } 143 | 144 | $*sockaddr_in := @cast(sockaddr, "sockaddr_in", "$^exec_path") 145 | return ntohs($*sockaddr_in->sin_port) 146 | } 147 | -------------------------------------------------------------------------------- /samples/ngx-lua-shdict-info.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | 4 | probe @pfunc(ngx_process_events_and_timers) 5 | { 6 | if (pid() == target()) { 7 | 8 | found = 0 9 | begin = local_clock_us() 10 | 11 | pagesize = @var("ngx_pagesize@ngx_alloc.c") 12 | part = &@var("ngx_cycle@ngx_cycle.c")->shared_memory->part 13 | $*part := @cast(part, "ngx_list_part_t") 14 | zone = $*part->elts 15 | for (i = 0; ; i++) { 16 | if (i >= $*part->nelts) { 17 | if ($*part->next == 0) { 18 | println("no next") 19 | break 20 | } 21 | 22 | part = $*part->next 23 | zone = $*part->elts 24 | i = 0 25 | } 26 | 27 | $*zone := @cast(zone, "ngx_shm_zone_t") 28 | 29 | ctx = $*zone[i]->data 30 | $*ctx := @cast(ctx, "ngx_http_lua_shdict_ctx_t", "$^exec_path") 31 | 32 | zone_name = user_string_n($*ctx->name->data, $*ctx->name->len) 33 | // zone_name = user_string_n($*ctx->name->data, 4) 34 | 35 | // printf("shm zone \"%s\"\n", zone_name) 36 | 37 | if (zone_name != "$^arg_dict") { 38 | continue; 39 | } 40 | 41 | shm = &$*zone[i]->shm 42 | 43 | $*shm := @cast(shm, "ngx_shm_t") 44 | 45 | init = $*zone[i]->init 46 | addr = $*shm->addr 47 | $*addr := @cast(addr, "ngx_slab_pool_t") 48 | addr2 = $*addr->addr 49 | 50 | if (addr != addr2) { 51 | error("shm zone \"" . zone_name . "\" is corrupted or " 52 | . "not yet loaded into the process memory space: " 53 | . "pool != pool->addr " 54 | . sprintf("(pool->addr: %p)", addr2)) 55 | exit() 56 | } 57 | 58 | init_name = usymname(init) 59 | owner = str_replace(str_replace(init_name, "_init", ""), "_zone", "") 60 | 61 | printf("shm zone \"%s\"\n", zone_name) 62 | printf(" owner: %s\n", owner) 63 | printf(" total size: %d KB\n", @cast(shm, "ngx_shm_t")->size / 1024) 64 | 65 | pages = 0 66 | free = &$*addr->free 67 | $*free := @cast(free, "ngx_slab_page_t") 68 | blocks = 0 69 | 70 | for (page = $*free->next; 71 | page && page != free; 72 | page = @cast(page, "ngx_slab_page_t")->next) 73 | { 74 | //printf("page: %p\n", page) 75 | pages += @cast(page, "ngx_slab_page_t")->slab 76 | if (++blocks % 1000 == 0) { 77 | printf(" free pages: %d KB (%d pages, %d blocks)\n", 78 | pages * pagesize / 1024, pages, blocks) 79 | } 80 | } 81 | 82 | printf(" free pages: %d KB (%d pages, %d blocks)\n", 83 | pages * pagesize / 1024, pages, blocks) 84 | 85 | sh = $*ctx->sh 86 | $*sh := @cast(sh, "ngx_http_lua_shdict_shctx_t", "$^exec_path") 87 | 88 | node = $*sh->rbtree->root 89 | sentinel = $*sh->rbtree->sentinel 90 | 91 | $*node := @cast(node, "ngx_rbtree_node_t") 92 | 93 | height = 0 94 | while (node != sentinel) { 95 | /* 96 | printf("root: %p, sentinel: %p\n", node, sentinel) 97 | 98 | sd = &node->color 99 | $*sd := @cast(sd, "ngx_http_lua_shdict_node_t") 100 | 101 | key_name = user_string_n($*sd->data, $*sd->key_len) 102 | printf("key name: \"%s\"\n", key_name) 103 | */ 104 | 105 | if ($*node->color == 0) { 106 | height++ 107 | } 108 | node = $*node->left 109 | } 110 | 111 | printf(" rbtree black height: %d\n", height) 112 | 113 | found = 1 114 | break 115 | } 116 | 117 | if (!found) { 118 | printf("shm zone \"%s\" not found.\n", "$^arg_dict") 119 | } 120 | 121 | elapsed = local_clock_us() - begin 122 | printf("\n%d microseconds elapsed in the probe handler.\n", elapsed) 123 | 124 | exit() 125 | 126 | } /* pid() == target() */ 127 | } 128 | 129 | probe begin 130 | { 131 | printf("Start tracing %d ($^exec_path)\n", target()) 132 | } 133 | -------------------------------------------------------------------------------- /samples/lj-gc-objs.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit 5 | 6 | @define TSTR %( 4 %) 7 | @define TUPVAL %( 5 %) 8 | @define TTHREAD %( 6 %) 9 | @define TPROTO %( 7 %) 10 | @define TFUNC %( 8 %) 11 | @define TTRACE %( 9 %) 12 | @define TCDATA %( 10 %) 13 | @define TTAB %( 11 %) 14 | @define TUDATA %( 12 %) 15 | 16 | @define GCSpause %( 0 %) 17 | @define GCSpropagate %( 1 %) 18 | @define GCSatomic %( 2 %) 19 | @define GCSsweepstring %( 3 %) 20 | @define GCSsweep %( 4 %) 21 | @define GCSfinalize %( 5 %) 22 | 23 | @define LJ_PAGESIZE %( 4096 %) 24 | @define LJ_NUM_CBPAGE %( 1 %) 25 | @define CALLBACK_MCODE_SIZE %( (@LJ_PAGESIZE * @LJ_NUM_CBPAGE) %) 26 | 27 | @define sizeof_GG_State %( &@cast(0, "GG_State", "$^libluajit_path")[1] %) 28 | @define sizeof_lua_State %( &@cast(0, "struct lua_State", "$^libluajit_path")[1] %) 29 | @define sizeof_GCRef %( &@cast(0, "GCRef", "$^libluajit_path")[1] %) 30 | @define sizeof_CType %( &@cast(0, "CType", "$^libluajit_path")[1] %) 31 | @define sizeof_CTState %( &@cast(0, "CTState", "$^libluajit_path")[1] %) 32 | 33 | global typenames 34 | global gcstates 35 | global gcobjs 36 | 37 | probe @pfunc(ngx_process_events_and_timers)?, timer.profile 38 | { 39 | if (pid() == target()) { 40 | if (@defined(@var("globalL", "$^exec_path"))) { 41 | L = @var("globalL", "$^exec_path") 42 | 43 | } else { 44 | L = ngx_lua_get_main_lua_vm() 45 | } 46 | 47 | if (L == 0) { 48 | printf("Failed to get the main Lua VM\n") 49 | exit() 50 | } 51 | 52 | process(L) 53 | } 54 | } 55 | 56 | function process(L) 57 | { 58 | begin = gettimeofday_us() 59 | 60 | g = luajit_G(L) 61 | $*g := @cast(g, "global_State", "$^libluajit_path") 62 | 63 | GG = g - &@cast(0, "GG_State", "$^libluajit_path")->g 64 | $*GG := @cast(GG, "GG_State", "$^libluajit_path") 65 | 66 | J = &$*GG->J 67 | $*J := @cast(J, "jit_State", "$^libluajit_path") 68 | 69 | mc_size = $*J->szallmcarea 70 | 71 | if (mc_size) { 72 | printf("main machine code area size: %d bytes\n", mc_size) 73 | } 74 | 75 | cts = $*g->ctype_state->ptr32 76 | $*cts := @cast(cts, "CTState", "$^libluajit_path") 77 | if (cts) { 78 | printf("C callback machine code size: %d bytes\n", @CALLBACK_MCODE_SIZE) 79 | } 80 | 81 | printf("GC total size: %d bytes\n", $*g->gc->total) 82 | 83 | $*o := @cast(o, "GCobj", "$^libluajit_path") 84 | for (p = &$*g->gc->root; p; p = &$*o->gch->nextgc) { 85 | o = luajit_gcref(p) 86 | if (o == 0) { 87 | break 88 | } 89 | //printf("gct: %d\n", $*o->gch->gct) 90 | gct = $*o->gch->gct 91 | size = luajit_objlen(o, gct, g) 92 | //printf("%s: %d\n", typenames[gct], size) 93 | gcobjs[$*o->gch->gct] <<< size 94 | } 95 | 96 | strmask = $*g->strmask 97 | strnum = $*g->strnum 98 | strhash = $*g->strhash 99 | $*strhash := @cast(strhash, "GCRef", "$^libluajit_path") 100 | 101 | n = 0 102 | done = 0 103 | for (i = 0; i <= strmask; i++) { 104 | p = &$*strhash[i] 105 | 106 | while (p) { 107 | o = luajit_gcref(p) 108 | if (o == 0) { 109 | break; 110 | } 111 | 112 | $*o := @cast(o, "GCobj", "$^libluajit_path") 113 | gct = $*o->gch->gct 114 | if (gct != @TSTR) { 115 | error(sprintf("Bad string type: %d", gct)) 116 | } 117 | size = luajit_objlen(o, @TSTR, g) 118 | //printf("%s: %d\n", typenames[@TSTR], size) 119 | gcobjs[@TSTR] <<< size 120 | 121 | if (++n == strnum) { 122 | done = 1 123 | break 124 | } 125 | 126 | p = &$*o->gch->nextgc 127 | } 128 | 129 | if (done) { 130 | break 131 | } 132 | } 133 | 134 | strhash_size = (strmask + 1) * @sizeof_GCRef 135 | 136 | //printf("strmask: %d, strnum: %d, strings: %d\n", strhash, strnum, n) 137 | printf("GC state: %s\n\n", gcstates[$*g->gc->state]) 138 | 139 | total = 0 140 | foreach (t in gcobjs- limit 100) { 141 | printf("%d %s objects: max=%d, avg=%d, min=%d, sum=%d (in bytes)\n", 142 | @count(gcobjs[t]), typenames[t], @max(gcobjs[t]), 143 | @avg(gcobjs[t]), @min(gcobjs[t]), @sum(gcobjs[t])) 144 | total += @sum(gcobjs[t]) 145 | } 146 | 147 | total += strhash_size + @sizeof_GG_State - @sizeof_lua_State 148 | 149 | jit_state_size = luajit_jit_state_size(J) 150 | if (jit_state_size) { 151 | total += jit_state_size 152 | printf("JIT state size: %d bytes\n", jit_state_size) 153 | } 154 | 155 | if (@defined($*g->tmpbuf->sz)) { 156 | /* for LuaJIT 2.0 */ 157 | sizetmpbuf = $*g->tmpbuf->sz 158 | 159 | } else { 160 | /* for LuaJIT 2.1 */ 161 | sizetmpbuf = $*g->tmpbuf->e->ptr32 - $*g->tmpbuf->b->ptr32 162 | } 163 | 164 | if (sizetmpbuf) { 165 | total += sizetmpbuf 166 | printf("global state tmpbuf size: %d bytes\n", sizetmpbuf) 167 | } 168 | 169 | if (cts) { 170 | ctype_state_size = $*cts->sizetab * @sizeof_CType 171 | ctype_state_size += @sizeof_CTState 172 | if (ctype_state_size) { 173 | total += ctype_state_size 174 | printf("C type state size: %d bytes\n", ctype_state_size) 175 | } 176 | } 177 | 178 | printf("\nMy GC walker detected for total %d bytes.\n", total) 179 | 180 | printf("%d microseconds elapsed in the probe handler.\n", 181 | gettimeofday_us() - begin) 182 | 183 | exit() 184 | } 185 | 186 | probe begin { 187 | typenames[@TSTR] = "string" 188 | typenames[@TUPVAL] = "upvalue" 189 | typenames[@TTHREAD] = "thread" 190 | typenames[@TPROTO] = "proto" 191 | typenames[@TFUNC] = "function" 192 | typenames[@TTRACE] = "trace" 193 | typenames[@TCDATA] = "cdata" 194 | typenames[@TTAB] = "table" 195 | typenames[@TUDATA] = "userdata" 196 | 197 | gcstates[@GCSpause] = "pause" 198 | gcstates[@GCSpropagate] = "propagate" 199 | gcstates[@GCSatomic] = "atomic" 200 | gcstates[@GCSsweepstring] = "sweep-string" 201 | gcstates[@GCSsweep] = "sweep" 202 | gcstates[@GCSfinalize] = "finalize" 203 | 204 | printf("Start tracing %d ($^exec_path)\n\n", target()) 205 | } 206 | -------------------------------------------------------------------------------- /samples/luajit21-gc64/lj-gc-objs.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use nginx.lua 4 | @use luajit_gc64 5 | 6 | @define TSTR %( 4 %) 7 | @define TUPVAL %( 5 %) 8 | @define TTHREAD %( 6 %) 9 | @define TPROTO %( 7 %) 10 | @define TFUNC %( 8 %) 11 | @define TTRACE %( 9 %) 12 | @define TCDATA %( 10 %) 13 | @define TTAB %( 11 %) 14 | @define TUDATA %( 12 %) 15 | 16 | @define GCSpause %( 0 %) 17 | @define GCSpropagate %( 1 %) 18 | @define GCSatomic %( 2 %) 19 | @define GCSsweepstring %( 3 %) 20 | @define GCSsweep %( 4 %) 21 | @define GCSfinalize %( 5 %) 22 | 23 | @define LJ_PAGESIZE %( 4096 %) 24 | @define LJ_NUM_CBPAGE %( 1 %) 25 | @define CALLBACK_MCODE_SIZE %( (@LJ_PAGESIZE * @LJ_NUM_CBPAGE) %) 26 | 27 | @define sizeof_GG_State %( &@cast(0, "GG_State", "$^libluajit_path")[1] %) 28 | @define sizeof_lua_State %( &@cast(0, "struct lua_State", "$^libluajit_path")[1] %) 29 | @define sizeof_GCRef %( &@cast(0, "GCRef", "$^libluajit_path")[1] %) 30 | @define sizeof_CType %( &@cast(0, "CType", "$^libluajit_path")[1] %) 31 | @define sizeof_CTState %( &@cast(0, "CTState", "$^libluajit_path")[1] %) 32 | 33 | global typenames 34 | global gcstates 35 | global gcobjs 36 | 37 | probe @pfunc(ngx_process_events_and_timers)?, timer.profile 38 | { 39 | if (pid() == target()) { 40 | if (@defined(@var("globalL", "$^exec_path"))) { 41 | L = @var("globalL", "$^exec_path") 42 | 43 | } else { 44 | L = ngx_lua_get_main_lua_vm() 45 | } 46 | 47 | if (L == 0) { 48 | printf("Failed to get the main Lua VM\n") 49 | exit() 50 | } 51 | 52 | process(L) 53 | } 54 | } 55 | 56 | function process(L) 57 | { 58 | begin = gettimeofday_us() 59 | 60 | g = luajit_G(L) 61 | $*g := @cast(g, "global_State", "$^libluajit_path") 62 | 63 | GG = g - &@cast(0, "GG_State", "$^libluajit_path")->g 64 | $*GG := @cast(GG, "GG_State", "$^libluajit_path") 65 | 66 | J = &$*GG->J 67 | $*J := @cast(J, "jit_State", "$^libluajit_path") 68 | 69 | mc_size = $*J->szallmcarea 70 | 71 | if (mc_size) { 72 | printf("main machine code area size: %d bytes\n", mc_size) 73 | } 74 | 75 | cts = $*g->ctype_state->ptr64 76 | $*cts := @cast(cts, "CTState", "$^libluajit_path") 77 | if (cts) { 78 | printf("C callback machine code size: %d bytes\n", @CALLBACK_MCODE_SIZE) 79 | } 80 | 81 | printf("GC total size: %d bytes\n", $*g->gc->total) 82 | 83 | $*o := @cast(o, "GCobj", "$^libluajit_path") 84 | for (p = &$*g->gc->root; p; p = &$*o->gch->nextgc) { 85 | o = luajit_gcref(p) 86 | if (o == 0) { 87 | break 88 | } 89 | //printf("gct: %d\n", $*o->gch->gct) 90 | gct = $*o->gch->gct 91 | size = luajit_objlen(o, gct, g) 92 | //printf("%s: %d\n", typenames[gct], size) 93 | gcobjs[$*o->gch->gct] <<< size 94 | } 95 | 96 | strmask = $*g->strmask 97 | strnum = $*g->strnum 98 | strhash = $*g->strhash 99 | $*strhash := @cast(strhash, "GCRef", "$^libluajit_path") 100 | 101 | n = 0 102 | done = 0 103 | for (i = 0; i <= strmask; i++) { 104 | p = &$*strhash[i] 105 | 106 | while (p) { 107 | o = luajit_gcref(p) 108 | if (o == 0) { 109 | break; 110 | } 111 | 112 | $*o := @cast(o, "GCobj", "$^libluajit_path") 113 | gct = $*o->gch->gct 114 | if (gct != @TSTR) { 115 | error(sprintf("Bad string type: %d", gct)) 116 | } 117 | size = luajit_objlen(o, @TSTR, g) 118 | //printf("%s: %d\n", typenames[@TSTR], size) 119 | gcobjs[@TSTR] <<< size 120 | 121 | if (++n == strnum) { 122 | done = 1 123 | break 124 | } 125 | 126 | p = &$*o->gch->nextgc 127 | } 128 | 129 | if (done) { 130 | break 131 | } 132 | } 133 | 134 | strhash_size = (strmask + 1) * @sizeof_GCRef 135 | 136 | //printf("strmask: %d, strnum: %d, strings: %d\n", strhash, strnum, n) 137 | printf("GC state: %s\n\n", gcstates[$*g->gc->state]) 138 | 139 | total = 0 140 | foreach (t in gcobjs- limit 100) { 141 | printf("%d %s objects: max=%d, avg=%d, min=%d, sum=%d (in bytes)\n", 142 | @count(gcobjs[t]), typenames[t], @max(gcobjs[t]), 143 | @avg(gcobjs[t]), @min(gcobjs[t]), @sum(gcobjs[t])) 144 | total += @sum(gcobjs[t]) 145 | } 146 | 147 | total += strhash_size + @sizeof_GG_State - @sizeof_lua_State 148 | 149 | jit_state_size = luajit_jit_state_size(J) 150 | if (jit_state_size) { 151 | total += jit_state_size 152 | printf("JIT state size: %d bytes\n", jit_state_size) 153 | } 154 | 155 | if (@defined($*g->tmpbuf->sz)) { 156 | /* for LuaJIT 2.0 */ 157 | sizetmpbuf = $*g->tmpbuf->sz 158 | 159 | } else { 160 | /* for LuaJIT 2.1 */ 161 | sizetmpbuf = $*g->tmpbuf->e->ptr64 - $*g->tmpbuf->b->ptr64 162 | } 163 | 164 | if (sizetmpbuf) { 165 | total += sizetmpbuf 166 | printf("global state tmpbuf size: %d bytes\n", sizetmpbuf) 167 | } 168 | 169 | if (cts) { 170 | ctype_state_size = $*cts->sizetab * @sizeof_CType 171 | ctype_state_size += @sizeof_CTState 172 | if (ctype_state_size) { 173 | total += ctype_state_size 174 | printf("C type state size: %d bytes\n", ctype_state_size) 175 | } 176 | } 177 | 178 | printf("\nMy GC walker detected for total %d bytes.\n", total) 179 | 180 | printf("%d microseconds elapsed in the probe handler.\n", 181 | gettimeofday_us() - begin) 182 | 183 | exit() 184 | } 185 | 186 | probe begin { 187 | typenames[@TSTR] = "string" 188 | typenames[@TUPVAL] = "upvalue" 189 | typenames[@TTHREAD] = "thread" 190 | typenames[@TPROTO] = "proto" 191 | typenames[@TFUNC] = "function" 192 | typenames[@TTRACE] = "trace" 193 | typenames[@TCDATA] = "cdata" 194 | typenames[@TTAB] = "table" 195 | typenames[@TUDATA] = "userdata" 196 | 197 | gcstates[@GCSpause] = "pause" 198 | gcstates[@GCSpropagate] = "propagate" 199 | gcstates[@GCSatomic] = "atomic" 200 | gcstates[@GCSsweepstring] = "sweep-string" 201 | gcstates[@GCSsweep] = "sweep" 202 | gcstates[@GCSfinalize] = "finalize" 203 | 204 | printf("Start tracing %d ($^exec_path)\n\n", target()) 205 | } 206 | -------------------------------------------------------------------------------- /samples/ngx-pcre-top.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | @use luajit 4 | 5 | @define now %( 6 | %( "$^arg_utime :default()" != "" %? 7 | cputime_to_usecs(task_utime()) 8 | %: 9 | gettimeofday_us() 10 | %) 11 | %) 12 | 13 | global regex 14 | global gmatch_regex 15 | global datalen 16 | global begin 17 | global exectimes 18 | global datalens 19 | global compiled 20 | global TL_TSTR = 4294967291 21 | global LJ_TUDATA = 4294967283 22 | global found = 0 23 | 24 | $*sizeof_TValue := &@cast(0, "TValue", "$^libluajit_path")[1] 25 | $*o := @cast(o, "TValue", "$^libluajit_path") 26 | $*L := @cast(L, "lua_State", "$^libluajit_path") 27 | $*gcobj := @cast(gcobj, "GCobj", "$^libluajit_path") 28 | 29 | probe begin { 30 | printf("Start tracing %d ($^exec_path)\n", target()) 31 | %( "$^arg_time :default()" != "" %? 32 | printf("Please wait for $^arg_time seconds...\n") 33 | %: 34 | printf("Hit Ctrl-C to end.\n") 35 | %) 36 | } 37 | 38 | probe process("$^exec_path").function("ngx_http_lua_ffi_exec_regex") 39 | { 40 | if (target() == pid() && regex == "") { 41 | re = $re 42 | $*re := @cast(re, "ngx_http_lua_regex_t", "$^exec_path") 43 | regex = user_string_warn($*re->pattern) 44 | //printf("regex: %s\n", regex) 45 | } 46 | } 47 | 48 | function gcref(gcr) 49 | { 50 | return @cast(gcr, "GCRef", "$^libluajit_path")->gcptr32 51 | } 52 | 53 | function gcval(o) 54 | { 55 | return gcref(&@cast(o, "TValue", "$^libluajit_path")->gcr) 56 | } 57 | 58 | function strdata(s) 59 | { 60 | return s + &@cast(0, "GCstr", "$^libluajit_path")[1] 61 | } 62 | 63 | probe process("$^exec_path").function("ngx_http_lua_ngx_re_gmatch_iterator") 64 | { 65 | if (target() == pid()) { 66 | L = $L 67 | fn = luajit_frame_func(L) 68 | $*fn := @cast(fn, "GCfunc", "$^libluajit_path") 69 | //printf("upvalues: %d\\n", $fn->c->nupvalues) 70 | if ($*fn->c->nupvalues >= 3) { 71 | o = &$*fn->c->upvalue[1] // the 2nd upvalue 72 | //printf("upvalue type: %d == $LJ_TUDATA\\n", $o->it) 73 | if (@cast(o, "TValue", "$^libluajit_path")->it == LJ_TUDATA) { 74 | gcobj = gcval(o) 75 | ud = &$*gcobj ->ud 76 | regex = compiled[ud] 77 | } 78 | } 79 | } 80 | } 81 | 82 | probe process("$^exec_path").function("ngx_http_lua_ngx_re_gmatch") 83 | { 84 | if (target() == pid()) { 85 | delete compiled // XXX this is a hack 86 | L = $L 87 | o = $*L->base + $*sizeof_TValue * (2 - 1) 88 | if (o < $*L->top && $*o->it == TL_TSTR) { 89 | gcobj = gcval(o) 90 | str = &$*gcobj->str 91 | $*str := &@cast(str, "GCstr", "$^libluajit_path") 92 | //printf("gmatch regex: %s\\n", user_string_n_warn(strdata(str), $*str->len)) 93 | gmatch_regex = user_string_n_warn(strdata(str), $*str->len) 94 | } else { 95 | gmatch_regex = "" 96 | } 97 | } 98 | } 99 | 100 | probe process("$^libluajit_path").function("lua_pushcclosure") { 101 | if (target() == pid() && gmatch_regex != "") { 102 | L = $L 103 | //compiled[$ctx->regex] = gmatch_regex 104 | o = $*L->top + $*sizeof_TValue * -2 105 | //printf("type %d == $LJ_TUDATA\\n", $o->it) 106 | LJ_TUDATA = 4294967283 107 | if ($*o->it == LJ_TUDATA) { 108 | gcobj = gcval(o) 109 | ud = &$*gcobj->ud 110 | compiled[ud] = gmatch_regex 111 | } 112 | gmatch_regex = "" 113 | } 114 | } 115 | 116 | probe process("$^exec_path").function("ngx_http_lua_ngx_re_gmatch").return 117 | { 118 | if (target() == pid()) { 119 | gmatch_regex = "" 120 | } 121 | } 122 | 123 | probe process("$^exec_path").function("ngx_http_lua_ngx_re_sub_helper"), 124 | process("$^exec_path").function("ngx_http_lua_ngx_re_match_helper") !, 125 | process("$^exec_path").function("ngx_http_lua_ngx_re_match") 126 | { 127 | if (target() == pid()) { 128 | L = $L 129 | o = $*L->base + $*sizeof_TValue * (2 - 1) 130 | if (o < $*L->top && $*o->it == TL_TSTR) { 131 | gcobj = gcval(o) 132 | str = &$*gcobj->str 133 | //printf("regex: %s\\n", user_string_n_warn(strdata(str), $*str->len)) 134 | regex = user_string_n_warn(strdata(str), $*str->len) 135 | } 136 | } 137 | } 138 | 139 | probe process("$^libpcre_path").function("pcre_exec") 140 | { 141 | if (target() == pid()) { 142 | begin = @now 143 | datalen = $length 144 | } 145 | } 146 | 147 | %( "$^arg_worst_time :default()" != "" %? 148 | 149 | probe process("$^libpcre_path").function("pcre_exec").return 150 | { 151 | if (target() == pid() && begin && regex != "") { 152 | elapsed = @now - begin 153 | max = exectimes[regex] 154 | if (max < elapsed) { 155 | exectimes[regex] = elapsed 156 | datalens[regex] = datalen 157 | } 158 | regex = "" 159 | elapsed = 0 160 | begin = 0 161 | found = 1 162 | } 163 | } 164 | 165 | probe end 166 | { 167 | if (!found) { 168 | println("\nNo pcre_exec() calls found so far.") 169 | 170 | } else { 171 | println("\nTop N regexes with worst running time:") 172 | 173 | i = 0 174 | foreach (regex in exectimes- limit 10) { 175 | i++ 176 | printf("%d. pattern /%s/: %dus (data size: %d)\n", 177 | i, regex, exectimes[regex], datalens[regex]) 178 | } 179 | } 180 | } 181 | 182 | %: 183 | 184 | probe process("$^libpcre_path").function("pcre_exec").return 185 | { 186 | if (target() == pid() && begin && regex != "") { 187 | elapsed = @now - begin 188 | exectimes[regex] += elapsed 189 | datalens[regex] += datalen 190 | regex = "" 191 | begin = 0 192 | found = 1 193 | } 194 | } 195 | 196 | probe end 197 | { 198 | if (!found) { 199 | println("\nNo pcre_exec() calls found so far.") 200 | 201 | } else { 202 | println("\nTop N regexes with longest total running time:") 203 | 204 | i = 0 205 | foreach (regex in exectimes- limit 10) { 206 | i++ 207 | printf("%d. pattern /%s/: %dus (total data size: %d)\n", 208 | i, regex, exectimes[regex], datalens[regex]) 209 | } 210 | } 211 | } 212 | %) 213 | 214 | %("$^arg_time" != "" %? 215 | probe timer.s($^arg_time) { 216 | exit() 217 | } 218 | %) 219 | -------------------------------------------------------------------------------- /samples/ngx-single-req-latency.sxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env stap++ 2 | 3 | /* 4 | * Copyright (C) Yichun Zhang (agentzh) 5 | */ 6 | 7 | @use nginx.request 8 | 9 | global matched_r 10 | global matched_pid 11 | 12 | global begin_time, init_conns, header_read_time 13 | 14 | global entered_rewrite_phase, rewrite_phase_time 15 | global entered_preaccess_phase, preaccess_phase_time 16 | global entered_access_phase, access_phase_time 17 | global entered_content_phase, content_phase_time 18 | global entered_log_phase 19 | 20 | global entered_upstream_connect, upstream_connect_time 21 | global entered_upstream_send, upstream_send_time 22 | global entered_upstream_read, upstream_read_time 23 | 24 | global seen_find_config 25 | 26 | @define eval_rewrite_phase_time %( 27 | if (entered_rewrite_phase) { 28 | rewrite_phase_time += now - entered_rewrite_phase 29 | entered_rewrite_phase = 0 30 | } 31 | %) 32 | 33 | @define eval_preaccess_phase_time %( 34 | if (entered_preaccess_phase) { 35 | preaccess_phase_time += now - entered_preaccess_phase 36 | entered_preaccess_phase = 0 37 | } 38 | %) 39 | 40 | @define eval_access_phase_time %( 41 | if (entered_access_phase) { 42 | access_phase_time += now - entered_access_phase 43 | entered_access_phase = 0 44 | } 45 | %) 46 | 47 | probe @pfunc(ngx_http_create_request) 48 | { 49 | if (!matched_r && $^pid_ok) { 50 | init_conns[$c, pid()] = gettimeofday_us() 51 | } 52 | } 53 | 54 | probe @pfunc(ngx_http_close_connection) { 55 | delete init_conns[$c, pid()] 56 | } 57 | 58 | probe @pfunc(ngx_http_process_request) 59 | { 60 | if (!matched_pid && $^pid_ok) { 61 | 62 | init_conn = init_conns[$r->connection, pid()] 63 | if (init_conn) { 64 | 65 | %( "$^arg_header :default()" == "" %? 66 | %( "$^arg_uri :default()" == "" %? 67 | matched_r = $r 68 | matched_pid = pid() 69 | header_read_time = gettimeofday_us() 70 | begin_time = init_conn 71 | %: 72 | if (ngx_req_uri($r) == "$^arg_uri") { 73 | matched_r = $r 74 | matched_pid = pid() 75 | header_read_time = gettimeofday_us() 76 | begin_time = init_conn 77 | } 78 | %) 79 | 80 | %: 81 | target_header = "$^arg_header" 82 | target_header_len = strlen(target_header) 83 | 84 | //printf("target header length: %d\n", target_header_len) 85 | 86 | part = &$r->headers_in->headers->part 87 | $*part := @cast(part, "ngx_list_part_t") 88 | 89 | header = $*part->elts 90 | $*header := @cast(header, "ngx_table_elt_t") 91 | 92 | for (i = 0; /* void */; i++) { 93 | 94 | if (i >= $*part->nelts) { 95 | if ($*part->next == 0) { 96 | break 97 | } 98 | 99 | part = $*part->next 100 | header = $*part->elts 101 | i = 0 102 | } 103 | 104 | if ($*header[i]->key->len == target_header_len 105 | && $*header[i]->key->data 106 | && user_string_n($*header[i]->key->data, $*header[i]->key->len) 107 | == target_header 108 | && $*header[i]->value->len > 0) 109 | { 110 | matched_r = $r 111 | matched_pid = pid() 112 | header_read_time = gettimeofday_us() 113 | begin_time = init_conns[$r->connection, matched_pid] 114 | break 115 | } 116 | } 117 | %) 118 | } 119 | } 120 | } 121 | 122 | probe @pfunc(ngx_http_core_find_config_phase) 123 | { 124 | if (pid() == matched_pid && $r == matched_r) { 125 | seen_find_config = 1 126 | 127 | /* for internal redirects: */ 128 | entered_rewrite_phase = 0 129 | entered_preaccess_phase = 0 130 | entered_access_phase = 0 131 | entered_content_phase = 0 132 | entered_upstream_connect = 0 133 | entered_upstream_send = 0 134 | entered_upstream_read = 0 135 | } 136 | } 137 | 138 | probe @pfunc(ngx_http_core_rewrite_phase) 139 | { 140 | if (pid() == matched_pid && $r == matched_r) { 141 | if (seen_find_config && !entered_rewrite_phase) { 142 | entered_rewrite_phase = gettimeofday_us() 143 | } 144 | } 145 | } 146 | 147 | probe @pfunc(ngx_http_core_generic_phase) 148 | { 149 | if (seen_find_config && pid() == matched_pid && $r == matched_r) { 150 | /* pre-access phase */ 151 | now = gettimeofday_us() 152 | 153 | @eval_rewrite_phase_time 154 | if (!entered_preaccess_phase) { 155 | entered_preaccess_phase = now 156 | } 157 | } 158 | } 159 | 160 | probe @pfunc(ngx_http_core_access_phase) 161 | { 162 | if (pid() == matched_pid && $r == matched_r) { 163 | now = gettimeofday_us() 164 | 165 | @eval_rewrite_phase_time 166 | @eval_preaccess_phase_time 167 | if (!entered_access_phase) { 168 | entered_access_phase = now 169 | } 170 | } 171 | } 172 | 173 | probe @pfunc(ngx_http_core_try_files_phase) 174 | { 175 | if (pid() == matched_pid && $r == matched_r) { 176 | now = gettimeofday_us() 177 | 178 | @eval_rewrite_phase_time 179 | @eval_preaccess_phase_time 180 | @eval_access_phase_time 181 | } 182 | } 183 | 184 | probe @pfunc(ngx_http_core_content_phase) 185 | { 186 | if (pid() == matched_pid && $r == matched_r) { 187 | now = gettimeofday_us() 188 | 189 | @eval_rewrite_phase_time 190 | @eval_preaccess_phase_time 191 | @eval_access_phase_time 192 | 193 | if (!entered_content_phase) { 194 | entered_content_phase = now 195 | } 196 | } 197 | } 198 | 199 | probe @pfunc(ngx_http_log_request) 200 | { 201 | pid = pid() 202 | if (pid == matched_pid && $r == matched_r) { 203 | now = gettimeofday_us() 204 | 205 | @eval_rewrite_phase_time 206 | @eval_preaccess_phase_time 207 | @eval_access_phase_time 208 | 209 | if (entered_content_phase) { 210 | content_phase_time += now - entered_content_phase 211 | } 212 | 213 | if (!entered_log_phase) { 214 | entered_log_phase = now 215 | } 216 | 217 | printf("[%d] pid:%d %s %s\n", now, pid, user_string_n($r->method_name->data, $r->method_name->len), 218 | $r->uri->data ? user_string_n($r->unparsed_uri->data, 219 | $r->unparsed_uri->len) 220 | : "") 221 | 222 | total = now - begin_time 223 | 224 | %( "$^arg_min :default()" != "" %? 225 | if (total >= $^arg_min) { 226 | %) 227 | 228 | printf(" total: %dus, accept() ~ header-read: %dus, rewrite: %dus, pre-access: %dus, access: %dus, content: %dus\n upstream: connect=%dus, time-to-first-byte=%dus, read=%dus\n", 229 | total, 230 | header_read_time - begin_time, 231 | rewrite_phase_time, 232 | preaccess_phase_time, 233 | access_phase_time, 234 | content_phase_time, 235 | upstream_connect_time, 236 | upstream_send_time, 237 | upstream_read_time) 238 | //printf("exiting...") 239 | exit() 240 | 241 | %( "$^arg_min :default()" != "" %? 242 | } else { 243 | begin_time = 0 244 | entered_log_phase = 0 245 | entered_rewrite_phase = 0 246 | entered_preaccess_phase = 0 247 | entered_access_phase = 0 248 | entered_content_phase = 0 249 | entered_upstream_connect = 0 250 | entered_upstream_send = 0 251 | entered_upstream_read = 0 252 | matched_r = 0 253 | matched_pid = 0 254 | seen_find_config = 0 255 | header_read_time = 0 256 | delete init_conns 257 | } 258 | %) 259 | } 260 | } 261 | 262 | /* 263 | probe @pfunc(ngx_http_log_request).return 264 | { 265 | if (pid() == matched_pid && $r == matched_r) { 266 | now = gettimeofday_us() 267 | log_phase_time = now - entered_log_phase 268 | entered_log_phase = 0 269 | printf("log: %dus\n", log_phase_time) 270 | exit() 271 | } 272 | } 273 | */ 274 | 275 | probe @pfunc(ngx_http_upstream_connect) 276 | { 277 | if (pid() == matched_pid && $r == matched_r) { 278 | entered_upstream_connect = gettimeofday_us() 279 | } 280 | } 281 | 282 | probe @pfunc(ngx_http_upstream_process_header) 283 | { 284 | if (entered_upstream_send && pid() == matched_pid && $r == matched_r) { 285 | now = gettimeofday_us() 286 | upstream_send_time += now - entered_upstream_send 287 | entered_upstream_read = 0 288 | content_phase_time = 0 289 | } 290 | } 291 | 292 | probe @pfunc(ngx_http_upstream_send_request) 293 | { 294 | if (entered_upstream_connect && pid() == matched_pid && $r == matched_r) { 295 | now = gettimeofday_us() 296 | upstream_connect_time += now - entered_upstream_connect 297 | entered_upstream_send = now 298 | } 299 | } 300 | 301 | probe @pfunc(ngx_http_upstream_finalize_request) 302 | { 303 | if (entered_upstream_read && pid() == matched_pid && $r == matched_r) { 304 | now = gettimeofday_us() 305 | upstream_read_time += now - entered_upstream_read 306 | } 307 | } 308 | 309 | probe begin { 310 | printf("Start tracing process $^target ($^exec_path)...\n\n") 311 | } 312 | 313 | probe timer.s(3) { 314 | if (!begin_time) { 315 | delete init_conns 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /stap++: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Getopt::Long qw( GetOptions :config no_ignore_case); 7 | use File::Spec (); 8 | use File::Temp qw( tempdir ); 9 | use FindBin (); 10 | 11 | sub quote_sh_args ($); 12 | 13 | GetOptions("D=s@", \(my $D_opts), 14 | "c=s", \(my $shcmd), 15 | "d=s@", \(my $d_opts), 16 | "arg=s%", \(my $args), 17 | "args", \(my $print_args), 18 | "dump-src", \(my $dump_src), 19 | "sample-pid=i", \(my $sample_pid), 20 | "exec=s", \(my $exec_path), 21 | "help", \(my $help), 22 | "master=i", \(my $master_pid), 23 | "x=i", \(my $pid), 24 | "e=s", \(my $src), 25 | "v", \(my $verbose), 26 | "skip-badvars", \(my $skip_badvars), 27 | "I=s@", \(my $Inc)) 28 | or die usage(); 29 | 30 | push @$Inc, '.', "$FindBin::Bin/tapset"; 31 | 32 | #warn "Inc: @$Inc\n"; 33 | 34 | if ($^O ne 'linux') { 35 | die "Only linux is supported but I am on $^O.\n"; 36 | } 37 | 38 | if ($print_args && $args && %$args) { 39 | die "No --arg is allowed when --args is specified.\n"; 40 | } 41 | 42 | my $ver = `stap -V 2>&1`; 43 | if (!defined $ver) { 44 | die "Systemtap not installed or its \"stap\" utility is not visible to the PATH environment: $!\n"; 45 | } 46 | 47 | if ($ver =~ /version\s+(\d+\.\d+)/i) { 48 | my $v = $1; 49 | my $min_ver = 2.3; 50 | if ($v < $min_ver) { 51 | die "ERROR: at least systemtap $min_ver is required but found $v\n"; 52 | } 53 | 54 | } else { 55 | die "ERROR: unknown version of systemtap:\n$ver\n"; 56 | } 57 | 58 | if ($help) { 59 | print usage(); 60 | exit; 61 | } 62 | 63 | my %set_D_opts; 64 | for my $opt (@$D_opts) { 65 | #warn "$opt\n"; 66 | if ($opt =~ /^([_A-Z]+)=/) { 67 | $set_D_opts{$1} = 1; 68 | } 69 | } 70 | 71 | if (!$set_D_opts{MAXACTION}) { 72 | push @$D_opts, "MAXACTION=100000"; 73 | } 74 | 75 | if (!$set_D_opts{MAXMAPENTRIES}) { 76 | push @$D_opts, "MAXMAPENTRIES=5000"; 77 | } 78 | 79 | if (!$set_D_opts{MAXBACKTRACE}) { 80 | push @$D_opts, "MAXBACKTRACE=200"; 81 | } 82 | 83 | my $infile; 84 | if (!defined $src) { 85 | $infile = shift 86 | or die "No input file specified.\n"; 87 | 88 | $src = read_src_file($infile); 89 | 90 | } else { 91 | $infile = "-e"; 92 | } 93 | 94 | my (%StdVars, %UsedLibs, %DSOs, %LibPaths, %UsedStdVars); 95 | 96 | my @stap_opts; 97 | 98 | if ($verbose) { 99 | push @stap_opts, "-v"; 100 | } 101 | 102 | if ($d_opts) { 103 | for my $opt (@$d_opts) { 104 | push @stap_opts, '-d', $opt; 105 | } 106 | } 107 | 108 | if (defined $skip_badvars) { 109 | push @stap_opts, "--skip-badvars"; 110 | } 111 | 112 | for my $opt (@$D_opts) { 113 | push @stap_opts, "-D$opt"; 114 | } 115 | 116 | if (defined $exec_path) { 117 | if (!-f $exec_path) { 118 | die "$exec_path not found.\n"; 119 | } 120 | 121 | $StdVars{exec_path} = $exec_path; 122 | push @stap_opts, '-d', $exec_path; 123 | 124 | open my $in, "ldd $exec_path|" 125 | or die "cannot run the command \"ldd $exec_path\": $!\n"; 126 | while (<$in>) { 127 | if (m{\s+(\/\S+\.so(?:\.\d+)*)}) { 128 | my $path = $1; 129 | #warn "Found DSO $path.\n"; 130 | $DSOs{$path} = 1; 131 | } 132 | } 133 | 134 | for my $path (sort keys %DSOs) { 135 | push @stap_opts, '-d', $path; 136 | } 137 | 138 | close $in; 139 | } 140 | 141 | if (defined $pid) { 142 | if (defined $master_pid) { 143 | die "-x and --master are exclusive.\n"; 144 | } 145 | 146 | push @stap_opts, "-x", $pid; 147 | my $exec_file = "/proc/$pid/exe"; 148 | if (!-f $exec_file) { 149 | die "Nginx process $pid is not running or ", 150 | "you do not have enough permissions.\n"; 151 | } 152 | 153 | $StdVars{pid_ok} = gen_pid_test_condition(); 154 | $StdVars{target} = $pid; 155 | 156 | if (!defined $exec_path) { 157 | $exec_path = readlink $exec_file; 158 | $StdVars{exec_path} = $exec_path; 159 | push @stap_opts, "-d", $exec_path; 160 | } 161 | 162 | process_dso($pid); 163 | } 164 | 165 | if (defined $master_pid) { 166 | #push @stap_opts, "-x", $master_pid; 167 | my $exec_file = "/proc/$master_pid/exe"; 168 | if (!-f $exec_file) { 169 | die "Nginx process $master_pid is not running or ", 170 | "you do not have enough permissions.\n"; 171 | } 172 | 173 | if (!defined $exec_path) { 174 | $exec_path = readlink $exec_file; 175 | $StdVars{exec_path} = $exec_path; 176 | push @stap_opts, "-d", $exec_path; 177 | } 178 | 179 | # process DSO files 180 | my @pids = get_child_processes($master_pid); 181 | if (@pids == 0) { 182 | die "No child processes found for $master_pid.\n"; 183 | } 184 | 185 | $StdVars{pid_ok} = gen_pid_test_condition(\@pids); 186 | $StdVars{target} = "@pids"; 187 | 188 | my $pid = $pids[0]; 189 | process_dso($pid); 190 | } 191 | 192 | if ($sample_pid) { 193 | process_dso($sample_pid); 194 | } 195 | 196 | if (!defined $StdVars{pid_ok} && defined $exec_path) { 197 | $StdVars{pid_ok} = '1'; 198 | } 199 | 200 | if (!defined $StdVars{target} && defined $exec_path) { 201 | $StdVars{target} = 'ANY'; 202 | } 203 | 204 | if (!$print_args) { 205 | while (my ($k, $v) = each %$args) { 206 | #warn "getting $k => $v"; 207 | $StdVars{"arg_$k"} = $v; 208 | } 209 | } 210 | 211 | my %used_args; 212 | 213 | my $stap_src = process_src($infile, $src); 214 | if ($dump_src) { 215 | print $stap_src; 216 | exit; 217 | } 218 | 219 | if ($print_args) { 220 | for my $name (sort keys %used_args) { 221 | my $default = $used_args{$name}; 222 | print "\t--arg $name=VALUE"; 223 | if (defined $default) { 224 | print " (default: $default)\n"; 225 | 226 | } else { 227 | print "\n"; 228 | } 229 | } 230 | exit; 231 | } 232 | 233 | for my $key (keys %$args) { 234 | if (!$UsedStdVars{"arg_$key"}) { 235 | my $val = $args->{$key}; 236 | if (!defined $val) { 237 | $val = ''; 238 | } 239 | warn "WARNING: \$^arg_$key is defined by \"--arg $key=$val\", " 240 | ."but never used.\n"; 241 | } 242 | } 243 | 244 | my $tmpdir = tempdir("stapxx-XXXXXXXX", CLEANUP => 1); 245 | while (my ($lib, $src) = each %UsedLibs) { 246 | my $outfile = "$tmpdir/$lib.stp"; 247 | open my $out, ">$outfile" 248 | or die "Cannot open $outfile for writing: $!\n"; 249 | print $out $src; 250 | close $out; 251 | } 252 | 253 | push @stap_opts, "-I", $tmpdir; 254 | 255 | if (defined $shcmd) { 256 | push @stap_opts, "-c", $shcmd; 257 | } 258 | 259 | my $cmd = "stap " . quote_sh_args(\@stap_opts) . " -"; 260 | #warn $cmd; 261 | open my $in, "|$cmd" 262 | or die "Cannot run stap: $!\n"; 263 | print $in $stap_src; 264 | close $in; 265 | 266 | sub eval_usr_var { 267 | my ($file, $usr_vars, $var) = @_; 268 | if (defined $usr_vars->{$var}) { 269 | return $usr_vars->{$var}; 270 | } 271 | 272 | die "$file: line $.: Undefined user varaible \$*$var.\n"; 273 | } 274 | 275 | sub eval_std_var { 276 | my ($file, $var, $trait_name, $trait_val) = @_; 277 | 278 | $UsedStdVars{$var} = 1; 279 | 280 | if (defined $StdVars{$var}) { 281 | return $StdVars{$var}; 282 | } 283 | 284 | if (defined $trait_name) { 285 | #warn "trait: $trait_name"; 286 | if ($trait_name eq 'default') { 287 | if ($print_args && $var =~ /^arg_(\w+)$/) { 288 | $used_args{$1} = $trait_val; 289 | } 290 | $StdVars{$var} = $trait_val; 291 | return $trait_val; 292 | 293 | } else { 294 | die "$file: line $.: unknown trait name: $trait_name\n"; 295 | } 296 | } 297 | 298 | if ($print_args) { 299 | if ($var =~ /^arg_(\w+)$/) { 300 | $used_args{$1} = undef; 301 | } 302 | return ''; 303 | } 304 | 305 | if ($var eq 'exec_path') { 306 | die "$file: line $.: \$^exec_path is used but neither -x ", 307 | "nor --exec is specified.\n"; 308 | 309 | } elsif ($var =~ /^arg_(\w+)$/) { 310 | die "$file: line $.: \$^$var is used but no --arg $1=VAL option is specified.\n"; 311 | 312 | } elsif ($var =~ /^(lib\w+)_path$/) { 313 | my $prefix = $1; 314 | my $libpath = find_dso_path($prefix); 315 | if (!$libpath) { 316 | warn "$file: line $.: $prefix is not found, assuming it is statically linked.\n"; 317 | if (!defined $StdVars{exec_path}) { 318 | die "No -x option is specified.\n"; 319 | } 320 | 321 | $LibPaths{$prefix} = $StdVars{exec_path}; 322 | return $StdVars{exec_path}; 323 | } 324 | 325 | return $libpath; 326 | 327 | } else { 328 | die "$file: line $.: Undefined built-in variable \$^$var.\n"; 329 | } 330 | } 331 | 332 | sub find_dso_path { 333 | my $pat = shift; 334 | 335 | my $path = $LibPaths{$pat}; 336 | if ($path) { 337 | return $path; 338 | } 339 | 340 | my $name; 341 | if ($pat !~ /^lib(\S+)/) { 342 | die "bad pattern: $pat"; 343 | } 344 | 345 | $name = $1; 346 | 347 | my $found_path; 348 | for my $path (sort keys %DSOs) { 349 | #warn "checking $path against $pat"; 350 | if ($path =~ m{\blib\Q$name\E[-.\d]*\.so(?:\.\d+)*$}) { 351 | $LibPaths{$pat} = $path; 352 | $found_path = $path; 353 | warn "Found exact match for $pat: $path\n"; 354 | last; 355 | } 356 | 357 | if ($path =~ m{\b(?:lib)?\Q$name\E[^/\s]*?\.so(?:\.\d+)*$}) { 358 | if ($found_path) { 359 | warn "Ignored ambiguous library $path for \"$pat\"\n"; 360 | next; 361 | } 362 | 363 | $LibPaths{$pat} = $path; 364 | $found_path = $path; 365 | } 366 | } 367 | 368 | return $found_path; 369 | } 370 | 371 | sub read_src_file { 372 | my $infile = shift; 373 | open my $in, $infile 374 | or die "Cannot open $infile for reading: $!\n"; 375 | my $src = do { local $/; <$in> }; 376 | close $in; 377 | return $src; 378 | } 379 | 380 | sub use_libs { 381 | my ($file, $libs) = @_; 382 | #warn "libs: $libs"; 383 | my @libs = split /\s*,\s*/, $libs; 384 | for my $lib (@libs) { 385 | #warn "processing $lib..."; 386 | my $path = join("/", split /\./, $lib) . ".sxx"; 387 | #warn $path; 388 | for my $dir (@$Inc) { 389 | my $abspath = File::Spec->catfile($dir, $path); 390 | #warn "Testing $abspath\n"; 391 | #warn "exist: ", (-f $abspath) ? 1 : 0; 392 | if (-f $abspath) { 393 | my $src = read_src_file($abspath); 394 | $UsedLibs{$lib} = process_src($abspath, $src); 395 | goto next_lib; 396 | } 397 | } 398 | die "$file: line $.: cannot find \@use library $lib\n"; 399 | next_lib: 400 | } 401 | return ""; 402 | } 403 | 404 | sub process_src { 405 | my ($file, $src) = @_; 406 | 407 | my %usr_vars; 408 | my @bits; 409 | 410 | my $libname_pat = qr/(?:\w+(?:\.\w+)*)/; 411 | # process the input file 412 | open my $in, '<', \$src or die $!; 413 | 414 | while (<$in>) { 415 | if ($. == 1 && /^\#!/) { 416 | $_ = "#!/usr/bin/env stap\n"; 417 | next; 418 | } 419 | 420 | s{\$\^(arg_\w+)(?:\s*:(\w+)\s*\((.*?)\))?}{eval_std_var($file, $1, $2, $3)}eg; 421 | s{\@pfunc\s*\(\s*(\w+)\s*\)}{process("\$^exec_path").function("$1")}g; 422 | s{\$\^(\w+|lib[-.\w]+_path)(?:\s*:(\w+)\s*\((.*?)\))?}{eval_std_var($file, $1, $2, $3)}eg; 423 | s{\$\*(\w+)\s*:=\s*(\&?\@(?:cast|var)\(.*?\)(?:\[\d+\])?)}{$usr_vars{$1} = $2; ""}eg; 424 | s{\$\*(\w+)}{eval_usr_var($file, \%usr_vars, $1)}eg; 425 | s{\@use\s+($libname_pat(?:\s*,\s*$libname_pat)*)}{use_libs($file, $1)}eg; 426 | 427 | } continue { 428 | push @bits, $_; 429 | } 430 | 431 | close $in; 432 | return join '', @bits; 433 | } 434 | 435 | sub usage { 436 | return <<'_EOC_'; 437 | Usage: 438 | stap++ [optoins] [infile] 439 | 440 | Options: 441 | --arg NM=VAL Specify extra user arguments (for $^arg_NM). 442 | --args Print all available arguments for --arg. 443 | -c CMD start the probes, run CMD, and exit when it finishes 444 | -d PATH Load debug info for the specified objects 445 | -D NM=VAL Emit macro definition into generated C code. 446 | -e SCRIPT Run given script. 447 | --exec PATH Specify the executable file path to be traced. 448 | --help Print this help. 449 | -I PATH Specify the stap++ tapset library search directory. 450 | --master PID Specify the master pid whose child processes are traced. 451 | --sample-pid PID Sample process to inspect DSO objects to load via -d 452 | -v Be verbose. 453 | -x PID Sets target() to PID (also for $^exec_path and $^libxxx_path). 454 | 455 | Examples: 456 | stap++ -x 12345 -e 'probe begin { println("hello") exit() }' 457 | stap++ -x 12345 infile.ss 458 | _EOC_ 459 | } 460 | 461 | sub get_child_processes { 462 | my $pid = shift; 463 | my @files = glob "/proc/[0-9]*/stat"; 464 | my @children; 465 | for my $file (@files) { 466 | #print "file: $file\n"; 467 | if ($file =~ m{^/proc/$pid/}) { 468 | next; 469 | } 470 | 471 | open my $in, $file or next; 472 | my $line = <$in>; 473 | close $in; 474 | if ($line =~ /^(\d+) \S+ \S+ (\d+)/) { 475 | my ($child, $parent) = ($1, $2); 476 | if ($parent eq $pid) { 477 | push @children, $child; 478 | } 479 | } 480 | } 481 | 482 | @children = sort { $a <=> $b } @children; 483 | return @children; 484 | } 485 | 486 | sub gen_pid_test_condition { 487 | my $pids = shift; 488 | if (!$pids) { 489 | return "pid() == target()"; 490 | } 491 | 492 | my @c; 493 | for my $pid (@$pids) { 494 | push @c, "pid() == $pid"; 495 | } 496 | return '(' . join(" || ", @c) . ')'; 497 | } 498 | 499 | sub process_dso { 500 | my $pid = shift; 501 | my $maps_file = "/proc/$pid/maps"; 502 | open my $in, $maps_file 503 | or die "Cannot open $maps_file for reading: $!\n"; 504 | 505 | while (<$in>) { 506 | if (m{\S+\.so(?:\.\d+)*$}) { 507 | my $path = $&; 508 | $DSOs{$path} = 1; 509 | #warn "seeing $path"; 510 | } 511 | } 512 | 513 | for my $path (sort keys %DSOs) { 514 | push @stap_opts, "-d", $path; 515 | } 516 | } 517 | 518 | sub quote_sh_args ($) { 519 | my ($args) = @_; 520 | for my $arg (@$args) { 521 | if ($arg =~ m{^[- "&%;,|?*.+=\w:/()]*$}) { 522 | if ($arg =~ /[ "&%;,|?*()]/) { 523 | $arg = "'$arg'"; 524 | } 525 | next; 526 | } 527 | $arg =~ s/\\/\\\\/g; 528 | $arg =~ s/'/\\'/g; 529 | $arg =~ s/\n/\\n/g; 530 | $arg =~ s/\r/\\r/g; 531 | $arg =~ s/\t/\\t/g; 532 | $arg = "\$'$arg'"; 533 | } 534 | return "@$args"; 535 | } 536 | 537 | -------------------------------------------------------------------------------- /tapset/luajit.sxx: -------------------------------------------------------------------------------- 1 | // module luajit 2 | 3 | 4 | @use nginx.lua 5 | 6 | 7 | $*pt := @cast(pt, "GCproto", "$^libluajit_path") 8 | 9 | 10 | //@define LJ_TLIGHTUD %( 4294967292 %) 11 | @define LJ_TLIGHTUD %( 4294901760 %) 12 | @define LJ_TSTR %( 4294967291 %) 13 | @define LJ_TUDATA %( 4294967283 %) 14 | @define LJ_TFUNC %( 4294967287 %) 15 | 16 | 17 | @define TSTR %( 4 %) 18 | @define TUPVAL %( 5 %) 19 | @define TTHREAD %( 6 %) 20 | @define TPROTO %( 7 %) 21 | @define TFUNC %( 8 %) 22 | @define TTRACE %( 9 %) 23 | @define TCDATA %( 10 %) 24 | @define TTAB %( 11 %) 25 | @define TUDATA %( 12 %) 26 | 27 | @define CTSHIFT_NUM %( 28 %) 28 | @define CT_HASSIZE %( 5 %) 29 | @define CT_ATTRIB %( 8 %) 30 | @define CTMASK_CID %( 0xffff %) 31 | 32 | @define FF_LUA %( 0 %) 33 | @define FF_C %( 1 %) 34 | 35 | @define FRAME_LUA %( 0 %) 36 | @define FRAME_CONT %( 2 %) 37 | @define FRAME_TYPE %( 3 %) 38 | @define FRAME_P %( 4 %) 39 | @define FRAME_TYPEP %( (@FRAME_TYPE|@FRAME_P) %) 40 | 41 | @define NO_BCPOS %( ~0 %) 42 | 43 | @define sizeof_GCtab %( &@cast(0, "GCtab", "$^libluajit_path")[1] %) 44 | @define sizeof_TValue %( &@cast(0, "TValue", "$^libluajit_path")[1] %) 45 | @define sizeof_lua_State %( &@cast(0, "struct lua_State", "$^libluajit_path")[1] %) 46 | @define sizeof_GCfunc %( &@cast(0, "union GCfunc", "$^libluajit_path")[1] %) 47 | @define sizeof_GCupval %( &@cast(0, "GCupval", "$^libluajit_path")[1] %) 48 | @define sizeof_GCstr %( &@cast(0, "GCstr", "$^libluajit_path")[1] %) 49 | @define sizeof_GCudata %( &@cast(0, "GCudata", "$^libluajit_path")[1] %) 50 | @define sizeof_Node %( &@cast(0, "Node", "$^libluajit_path")[1] %) 51 | @define sizeof_GCfuncC %( &@cast(0, "GCfuncC", "$^libluajit_path")[1] %) 52 | @define sizeof_GCfuncL %( &@cast(0, "GCfuncL", "$^libluajit_path")[1] %) 53 | @define sizeof_GCRef %( &@cast(0, "GCRef", "$^libluajit_path")[1] %) 54 | @define sizeof_GCproto %( &@cast(0, "GCproto", "$^libluajit_path")[1] %) 55 | @define sizeof_GCtrace %( &@cast(0, "GCtrace", "$^libluajit_path")[1] %) 56 | @define sizeof_GCcdata %( &@cast(0, "GCcdata", "$^libluajit_path")[1] %) 57 | @define sizeof_IRIns %( &@cast(0, "IRIns", "$^libluajit_path")[1] %) 58 | @define sizeof_IRRef %( &@cast(0, "IRRef", "$^libluajit_path")[1] %) 59 | @define sizeof_SnapShot %( &@cast(0, "SnapShot", "$^libluajit_path")[1] %) 60 | @define sizeof_SnapEntry %( &@cast(0, "SnapEntry", "$^libluajit_path")[1] %) 61 | @define sizeof_K64Array %( &@cast(0, "K64Array", "$^libluajit_path")[1] %) 62 | @define sizeof_ptr %( &@cast(0, "global_State", "$^libluajit_path")->strmask %) 63 | @define sizeof_GCcdataVar %( &@cast(0, "GCcdataVar", "$^libluajit_path")[1] %) 64 | @define sizeof_BCIns %( &@cast(0, "BCIns", "$^libluajit_path")[1] %) 65 | 66 | @define strdata(s) %( 67 | (@s + @sizeof_GCstr) 68 | %) 69 | 70 | @define frame_gc(frame) %( 71 | @cast(@frame, "TValue", "$^libluajit_path")->fr->func->gcptr32 72 | %) 73 | 74 | @define frame_ftsz(tv) %( 75 | @cast(@tv, "TValue", "$^libluajit_path")->fr->tp->ftsz 76 | %) 77 | 78 | @define frame_type(f) %( 79 | (@frame_ftsz(@f) & @FRAME_TYPE) 80 | %) 81 | 82 | @define frame_typep(f) %( 83 | (@frame_ftsz(@f) & @FRAME_TYPEP) 84 | %) 85 | 86 | @define frame_islua(f) %( 87 | (@frame_type(@f) == @FRAME_LUA) 88 | %) 89 | 90 | @define frame_iscont(f) %( 91 | (@frame_typep(@f) == @FRAME_CONT) 92 | %) 93 | 94 | @define frame_pc(tv) %( 95 | @cast(@tv, "TValue", "$^libluajit_path")->fr->tp->pcr->ptr32 96 | %) 97 | 98 | @define frame_contpc(f) %( 99 | (@frame_pc(@f) - 1 * @sizeof_BCIns) 100 | %) 101 | 102 | @define bc_a(i) %( 103 | ((@i >> 8) & 0xff) 104 | %) 105 | 106 | @define frame_prevl(f) %( 107 | (@f - (1 + @bc_a(user_uint32(@frame_pc(@f) - 4))) * @sizeof_TValue) 108 | %) 109 | 110 | @define FRAME_VARG %( 3 %) 111 | 112 | @define frame_isvarg(f) %( 113 | (@frame_typep(@f) == @FRAME_VARG) 114 | %) 115 | 116 | @define frame_sized(f) %( 117 | (@frame_ftsz(@f) & ~@FRAME_TYPEP) 118 | %) 119 | 120 | @define frame_prevd(f) %( 121 | (@f - @frame_sized(@f)) 122 | %) 123 | 124 | @define isluafunc(fn) %( 125 | (@cast(@fn, "GCfunc", "$^libluajit_path")->c->ffid == @FF_LUA) 126 | %) 127 | 128 | @define isffunc(fn) %( 129 | (@cast(@fn, "GCfunc", "$^libluajit_path")->c->ffid > @FF_C) 130 | %) 131 | 132 | @define funcproto(fn) %( 133 | (@cast(@fn, "GCfunc", "$^libluajit_path")->l->pc->ptr32 - @sizeof_GCproto) 134 | %) 135 | 136 | @define proto_bc(pt) %( 137 | (@pt + @sizeof_GCproto) 138 | %) 139 | 140 | @define proto_bcpos(pt, pc) %( 141 | ((@pc - @proto_bc(@pt)) / @sizeof_BCIns) 142 | %) 143 | 144 | @define proto_lineinfo(pt) %( 145 | @cast(@pt, "GCproto", "$^libluajit_path")->lineinfo->ptr32 146 | %) 147 | 148 | 149 | global luajit_cfunc_cache 150 | 151 | 152 | function luajit_frame_func(f) { 153 | gco = @frame_gc(f) 154 | $*gco := @cast(gco, "GCobj", "$^libluajit_path") 155 | return &$*gco->fn 156 | } 157 | 158 | 159 | function luajit_G(L) 160 | { 161 | $*L := @cast(L, "lua_State", "$^libluajit_path") 162 | return $*L->glref->ptr32 163 | } 164 | 165 | 166 | function luajit_gcref(r) 167 | { 168 | $*r := @cast(r, "GCRef", "$^libluajit_path") 169 | return $*r->gcptr32 170 | } 171 | 172 | 173 | function luajit_objlen(o, gct, g) 174 | { 175 | $*o := @cast(o, "GCobj", "$^libluajit_path") 176 | if (gct == @TSTR) { 177 | /* 178 | if ($*o->str->len == 0) { 179 | printf("empty string found.\n") 180 | } 181 | */ 182 | return $*o->str->len + 1 + @sizeof_GCstr 183 | } 184 | 185 | if (gct == @TTAB) { 186 | t = &$*o->tab 187 | $*t := @cast(t, "GCtab", "$^libluajit_path") 188 | asize = $*t->asize 189 | hmask = $*t->hmask 190 | 191 | n = 0 192 | if (hmask > 0) { 193 | n += @sizeof_Node * (hmask + 1) 194 | } 195 | 196 | if (asize > 0 && $*t->colo <= 0) { 197 | n += @sizeof_TValue * asize 198 | } 199 | 200 | if ($*t->colo) { 201 | n += ($*t->colo & 0x7f) * @sizeof_TValue + @sizeof_GCtab 202 | 203 | } else { 204 | n += @sizeof_GCtab 205 | } 206 | 207 | return n 208 | } 209 | 210 | if (gct == @TUDATA) { 211 | return $*o->ud->len + @sizeof_GCudata 212 | } 213 | 214 | if (gct == @TPROTO) { 215 | return $*o->pt->sizept 216 | } 217 | 218 | if (gct == @TTHREAD) { 219 | L = &$*o->th 220 | //printf("open upval: %p\n", luajit_gcref(&$*L->openupval)) 221 | //printf("lua_State: %d\n", @sizeof_lua_State) 222 | n = @sizeof_lua_State + $*L->stacksize * @sizeof_TValue 223 | p = &$*L->openupval 224 | while (p) { 225 | o = luajit_gcref(p) 226 | if (o == 0) { 227 | break; 228 | } 229 | 230 | $*o := @cast(o, "GCobj", "$^libluajit_path") 231 | gct = $*o->gch->gct 232 | size = luajit_objlen(o, gct, g) 233 | //printf("%s: %d\n", typenames[@TSTR], size) 234 | n += size 235 | p = &$*o->gch->nextgc 236 | } 237 | 238 | return n 239 | } 240 | 241 | if (gct == @TFUNC) { 242 | fn = &$*o->fn 243 | $*fn := @cast(fn, "GCfunc", "$^libluajit_path"); 244 | if (@isluafunc(fn)) { 245 | n = $*fn->l->nupvalues 246 | //n = 0 247 | return @sizeof_GCfuncL - @sizeof_GCRef + @sizeof_GCRef * n 248 | } 249 | 250 | n = $*fn->c->nupvalues 251 | //n = 0 252 | return @sizeof_GCfuncC - @sizeof_TValue + @sizeof_TValue * n 253 | } 254 | 255 | if (gct == @TUPVAL) { 256 | return @sizeof_GCupval 257 | } 258 | 259 | if (gct == @TTRACE) { 260 | T = o 261 | $*T := @cast(T, "GCtrace", "$^libluajit_path") 262 | return ((@sizeof_GCtrace + 7) & ~7) 263 | + ($*T->nins - $*T->nk) * @sizeof_IRIns 264 | + $*T->nsnap * @sizeof_SnapShot 265 | + $*T->nsnapmap * @sizeof_SnapEntry 266 | } 267 | 268 | $*g := @cast(g, "global_State", "$^libluajit_path") 269 | 270 | if (gct == @TCDATA) { 271 | cd = o 272 | $*cd := @cast(cd, "GCcdata", "$^libluajit_path") 273 | if ($*cd->marked & 0x80) { 274 | /* cdata is a vector */ 275 | cdatav = cd - @sizeof_GCcdataVar 276 | $*cdatav := @cast(cdatav, "GCcdataVar", "$^libluajit_path") 277 | return $*cdatav->len + $*cdatav->extra 278 | } 279 | 280 | /* cdata is not a vector */ 281 | cts = $*g->ctype_state->ptr32 282 | $*cts := @cast(cts, "CTState", "$^libluajit_path") 283 | id = $*cd->ctypeid 284 | ct = &$*cts->tab[id] 285 | $*ct := @cast(ct, "CType", "$^libluajit_path") 286 | while (($*ct->info >> @CTSHIFT_NUM) == @CT_ATTRIB) { 287 | //printf("XXX skipping c type attribute\n") 288 | ct = &$*cts->tab[$*ct->info & @CTMASK_CID] 289 | } 290 | 291 | if (($*ct->info >> @CTSHIFT_NUM) <= @CT_HASSIZE) { 292 | sz = $*ct->size 293 | 294 | } else { 295 | sz = @sizeof_ptr 296 | } 297 | 298 | //printf("GCcdata size: %d\n", @sizeof_GCcdata) 299 | return @sizeof_GCcdata + sz 300 | } 301 | 302 | return 0 303 | } 304 | 305 | 306 | function luajit_jit_state_size(J) 307 | { 308 | $*J := @cast(J, "jit_State", "$^libluajit_path") 309 | 310 | ir_k64_size = 0 311 | // In the newer revision of luajit, 64-bit constants are represented as an 312 | // array of TValue. Uncomment following code if the luajit being used 313 | // is using linked list to represent 64-bit constants. As the 64-bit 314 | // constants don't take lots of memory, it does not seems to be a big deal 315 | // if you don't uncomment following code for the older luajit. 316 | // 317 | /* 318 | $*k := @cast(k, "K64Array", "$^libluajit_path") 319 | for (k = $*J->k64->ptr32; k; ) { 320 | ir_k64_size += @sizeof_K64Array 321 | k = $*k->next->ptr32 322 | } 323 | if (ir_k64_size) { 324 | //printf("64-bit constants: %d\n", ir_k64_size) 325 | } 326 | */ 327 | 328 | sizesnapmap = $*J->sizesnapmap * @sizeof_SnapEntry 329 | if (sizesnapmap) { 330 | //printf("JIT snap map buffer size: %d\n", sizesnapmap) 331 | } 332 | 333 | sizesnap = $*J->sizesnap * @sizeof_SnapShot 334 | if (sizesnap) { 335 | //printf("JIT snap buffer size: %d\n", sizesnap) 336 | } 337 | 338 | sizeirbuf = ($*J->irtoplim - $*J->irbotlim) * @sizeof_IRIns 339 | if (sizeirbuf) { 340 | //printf("JIT IR buffer size: %d\n", sizeirbuf) 341 | } 342 | 343 | sizetrace = $*J->sizetrace * @sizeof_GCRef 344 | if (sizetrace) { 345 | //printf("JIT trace array size: %d\n", sizetrace) 346 | } 347 | 348 | return ir_k64_size + sizesnapmap + sizesnap + sizeirbuf + sizetrace 349 | } 350 | 351 | 352 | /* returns a TValue* pointer */ 353 | function luajit_index2adr(L, idx) 354 | { 355 | if (idx > 0) { 356 | o = $*L->base + (idx - 1) * @sizeof_TValue 357 | return o < $*L->top ? o : 0 358 | } 359 | 360 | top = $*L->top 361 | 362 | if (idx != 0 && -idx <= top - $*L->base) { 363 | return top + idx * @sizeof_TValue; 364 | } 365 | 366 | return 0 367 | } 368 | 369 | 370 | /* convert GCstr to stap string */ 371 | function luajit_unbox_gcstr(gcs) 372 | { 373 | if (gcs == 0) { 374 | return "" 375 | } 376 | 377 | $*gcs := @cast(gcs, "GCstr", "$^libluajit_path") 378 | src = @strdata(gcs) 379 | return user_string_n_warn(src, $*gcs->len) 380 | } 381 | 382 | 383 | function luajit_tostring(L, idx) 384 | { 385 | $*o := @cast(o, "TValue", "$^libluajit_path") 386 | o = luajit_index2adr(L, idx) 387 | if (o == 0) { 388 | return "" 389 | } 390 | 391 | if ($*o->it == @LJ_TSTR) { 392 | gco = $*o->gcr->gcptr32 393 | $*gco := @cast(gco, "GCobj", "$^libluajit_path") 394 | s = &$*gco->str 395 | $*s := @cast(s, "GCstr", "$^libluajit_path") 396 | return user_string_n(s + @sizeof_GCstr, $*s->len) 397 | } 398 | 399 | return "" 400 | } 401 | 402 | 403 | function luajit_tostringlen(L, idx) 404 | { 405 | $*o := @cast(o, "TValue", "$^libluajit_path") 406 | o = luajit_index2adr(L, idx) 407 | if (o == 0) { 408 | return -1 409 | } 410 | 411 | if ($*o->it == @LJ_TSTR) { 412 | gco = $*o->gcr->gcptr32 413 | $*gco := @cast(gco, "GCobj", "$^libluajit_path") 414 | s = &$*gco->str 415 | $*s := @cast(s, "GCstr", "$^libluajit_path") 416 | return $*s->len 417 | } 418 | 419 | return -1 420 | } 421 | 422 | 423 | function luajit_touserdata(L, idx) 424 | { 425 | $*o := @cast(o, "TValue", "$^libluajit_path") 426 | o = luajit_index2adr(L, idx) 427 | if (o == 0) { 428 | return 0 429 | } 430 | 431 | //printf("udata type: %d (%d)\n", $*o->it, ($*o->it >> 15)) 432 | 433 | if ($*o->it == @LJ_TUDATA) { 434 | gco = $*o->gcr->gcptr32 435 | ud = &$*gco->ud 436 | $*ud := @cast(ud, "GCudata", "$^libluajit_path") 437 | return ud + @sizeof_GCudata 438 | } 439 | 440 | if ($*o->it == @LJ_TLIGHTUD) { 441 | return $*o->u64 & 0x7fffffffffff 442 | } 443 | 444 | return 0 445 | } 446 | 447 | 448 | function luajit_proto_chunkname(pt) { 449 | gco = $*pt->chunkname->gcptr32 450 | return &$*gco->str 451 | } 452 | 453 | 454 | function luajit_debug_framepc(L, T, fn, pt, nextframe) { 455 | //printf("debug framepc: L=%p, fn=%p, nextframe = %p\\n", L, fn, nextframe) 456 | if (nextframe == 0) { 457 | return @NO_BCPOS 458 | } 459 | 460 | if (@frame_islua(nextframe)) { 461 | ins = @frame_pc(nextframe); 462 | //printf("frame is lua, ins = %p\\n", ins) 463 | 464 | } else if (@frame_iscont(nextframe)) { 465 | //println("frame is cont") 466 | ins = @frame_contpc(nextframe) 467 | 468 | } else { 469 | /* TODO: add support for cframe */ 470 | } 471 | 472 | //printf("debug framepc: ins = %p\\n", ins) 473 | pos = @proto_bcpos(pt, ins) - 1; 474 | sizebc = $*pt->sizebc 475 | if (pos > sizebc) { 476 | //printf("Undo the effects of lj_trace_exit for JLOOP.\n") 477 | if (!T) { 478 | T = ins - 1 * @sizeof_BCIns - &@cast(0, "GCtrace", "$^libluajit_path")->startins; 479 | } 480 | //printf("startpc: %d\n", $*T->startpc->ptr32) 481 | pos = @proto_bcpos(pt, $*T->startpc->ptr32); 482 | //printf("Found adjusted position: %d\n", pos) 483 | } 484 | return pos; 485 | } 486 | 487 | 488 | function luajit_debug_line(pt, pc) 489 | { 490 | lineinfo = @proto_lineinfo(pt) 491 | //printf("lj_debug_line: lineinfo = %p, %x <= %x\\n", lineinfo, 492 | //pc, $pt->sizebc) 493 | sizebc = $*pt->sizebc 494 | 495 | if (pc <= sizebc && lineinfo) { 496 | first = $*pt->firstline 497 | 498 | if (pc == sizebc) { 499 | return first + $*pt->numline 500 | } 501 | 502 | if (pc-- == 0) { 503 | return first 504 | } 505 | 506 | if ($*pt->numline < 256) { 507 | return first + @cast(lineinfo, "uint8_t", "$^libluajit_path")[pc] 508 | } 509 | 510 | if ($*pt->numline < 65536) { 511 | return first + @cast(lineinfo, "uint16_t", "$^libluajit_path")[pc] 512 | } 513 | 514 | return first + @cast(lineinfo, "uint32_t", "$^libluajit_path")[pc] 515 | } 516 | 517 | return -1 518 | } 519 | 520 | 521 | function luajit_debug_frameline(L, T, fn, pt, nextframe) 522 | { 523 | pc = luajit_debug_framepc(L, T, fn, pt, nextframe) 524 | if (pc != @NO_BCPOS) { 525 | if (pc <= $*pt->sizebc) { 526 | return luajit_debug_line(pt, pc) 527 | } 528 | } 529 | 530 | return -1 531 | } 532 | 533 | 534 | function luajit_debug_dumpstack(L, T, depth, base, simple) 535 | { 536 | level = 0 537 | dir = 1 538 | if (depth < 0) { 539 | level = ~depth 540 | depth = dir = -1 541 | } 542 | bt = "" 543 | while (level != depth) { 544 | /* lj_debug_frame(L, level, &size) {{{ */ 545 | bot = $*L->stack->ptr32 546 | found_frame = 0 547 | tmp_level = level 548 | /* Traverse frames backwards. */ 549 | for (nextframe = frame = base - @sizeof_TValue; frame > bot; ) { 550 | if (@frame_gc(frame) == L) { 551 | tmp_level++ 552 | } 553 | 554 | if (tmp_level-- == 0) { 555 | size = (nextframe - frame) / @sizeof_TValue 556 | found_frame = 1 557 | break 558 | } 559 | nextframe = frame 560 | if (@frame_islua(frame)) { 561 | frame = @frame_prevl(frame) 562 | 563 | } else { 564 | if (@frame_isvarg(frame)) { 565 | tmp_level++; /* Skip vararg pseudo-frame. */ 566 | } 567 | 568 | frame = @frame_prevd(frame); 569 | } 570 | } 571 | 572 | if (!found_frame) { 573 | frame = 0 574 | size = tmp_level 575 | } 576 | /* }}} */ 577 | 578 | if (frame) { 579 | //nextframe = size ? frame + size * @sizeof_TValue : 0 580 | fn = luajit_frame_func(frame) 581 | if (fn == 0) { 582 | return "" 583 | } 584 | if (@isluafunc(fn)) { 585 | pt = @funcproto(fn) 586 | 587 | if (simple) { 588 | line = $*pt->firstline 589 | 590 | } else { 591 | line = luajit_debug_frameline(L, T, fn, pt, nextframe) 592 | if (line < 0) { 593 | line = $*pt->firstline 594 | //printf("using firstline %d\n", line) 595 | 596 | } else { 597 | //printf("GOT the lineno %d\n", line) 598 | } 599 | } 600 | 601 | name = luajit_proto_chunkname(pt) /* GCstr *name */ 602 | if (name == 0) { 603 | return "" 604 | } 605 | 606 | path = luajit_unbox_gcstr(name) 607 | bt .= sprintf("%s:%d\n", path, line) 608 | 609 | } else if (@isffunc(fn)) { 610 | bt .= sprintf("builtin#%d\n", $*fn->c->ffid) 611 | 612 | } else { 613 | /* C function */ 614 | cfunc = $*fn->c->f 615 | sym = luajit_cfunc_cache[cfunc] 616 | if (sym == "") { 617 | sym = sprintf("C:%s\n", usymname(cfunc)) 618 | luajit_cfunc_cache[cfunc] = sym 619 | } 620 | bt .= sym 621 | } 622 | 623 | } else if (dir == 1) { 624 | break 625 | 626 | } else { 627 | level -= size 628 | } 629 | level += dir 630 | } 631 | 632 | return bt 633 | } 634 | 635 | 636 | function luajit_cur_thread(g) 637 | { 638 | $*g := @cast(g, "global_State", "$^libluajit_path") 639 | gco = $*g->cur_L->gcptr32 640 | if (gco == 0) { 641 | return 0 642 | } 643 | 644 | return &$*gco->th 645 | } 646 | 647 | 648 | function luajit_get_trace(g, traceno) 649 | { 650 | GG = g - &@cast(0, "GG_State", "$^libluajit_path")->g 651 | $*GG := @cast(GG, "GG_State", "$^libluajit_path") 652 | J = &$*GG->J 653 | $*J := @cast(J, "jit_State", "$^libluajit_path") 654 | if (J == 0) { 655 | return 0 656 | } 657 | 658 | return $*J->trace[traceno]->gcptr32 659 | } 660 | 661 | 662 | function luajit_trace_starting_func(g, trace) 663 | { 664 | $*trace := @cast(trace, "GCtrace", "$^libluajit_path") 665 | gco = $*trace->startpt->gcptr32 666 | if (gco == 0) { 667 | return "" 668 | } 669 | 670 | pt = &$*gco->pt 671 | 672 | firstline = $*pt->firstline 673 | 674 | name = luajit_proto_chunkname(pt) /* GCstr *name */ 675 | path = luajit_unbox_gcstr(name) 676 | 677 | return sprintf("%s:%d", path, firstline) 678 | } 679 | 680 | 681 | function luajit_print_backtrace(simple) 682 | { 683 | if (@defined(@var("globalL", "$^exec_path"))) { 684 | mL = @var("globalL", "$^exec_path") 685 | 686 | } else { 687 | mL = ngx_lua_get_main_lua_vm() 688 | } 689 | 690 | if (mL == 0) { 691 | return "" 692 | } 693 | 694 | g = luajit_G(mL) 695 | if (g == 0) { 696 | return "" 697 | } 698 | 699 | L = luajit_cur_thread(g) 700 | if (L == 0) { 701 | return "" 702 | } 703 | 704 | return luajit_backtrace(L, g, simple) 705 | } 706 | 707 | 708 | function luajit_backtrace(L, g, simple) 709 | { 710 | bt = "" 711 | 712 | vmstate = $*g->vmstate 713 | if (vmstate >= 0 || (vmstate == -3 && $*g->jit_base->ptr32)) { 714 | /* compiled Lua code */ 715 | 716 | T = luajit_get_trace(g, vmstate) 717 | if (T) { 718 | if (simple) { 719 | func = luajit_trace_starting_func(g, T) 720 | if (func != "") { 721 | bt .= sprintf("T:%s\n", func) 722 | } 723 | 724 | } else { 725 | //printf("start pc: %d\n", $*T->startpc->ptr32) 726 | } 727 | 728 | #warn(sprintf("bt: %s", bt)) 729 | } 730 | 731 | base = $*g->jit_base->ptr32 732 | if (!base) { 733 | return bt 734 | } 735 | 736 | bt .= luajit_debug_dumpstack(L, T, $^arg_depth :default(30), base, simple) 737 | /* 738 | if (bt != "") { 739 | warn(sprintf("JIT backtrace: %s\n", bt)) 740 | } 741 | */ 742 | 743 | /* 744 | if (vmstate == -3) { 745 | printf("HIT JIT GC: %s===\n", bt) 746 | } 747 | */ 748 | 749 | } else { 750 | 751 | if (vmstate == -1 && !$*L->cframe) { 752 | return "" 753 | } 754 | 755 | if (vmstate == -1 || vmstate == -2 || vmstate == -3) { 756 | base = $*L->base 757 | bt .= luajit_debug_dumpstack(L, 0, $^arg_depth, base, simple) 758 | } 759 | } 760 | 761 | return bt 762 | } 763 | 764 | 765 | function luajit_vm_state(g) 766 | { 767 | return $*g->vmstate 768 | } 769 | 770 | 771 | function luajit_find_gcstr(g, str) 772 | { 773 | len = strlen(str) 774 | strmask = $*g->strmask 775 | strnum = $*g->strnum 776 | strhash = $*g->strhash 777 | $*strhash := @cast(strhash, "GCRef", "$^libluajit_path") 778 | ret = 0 779 | 780 | n = 0 781 | done = 0 782 | for (i = 0; i <= strmask; i++) { 783 | p = &$*strhash[i] 784 | 785 | while (p) { 786 | o = luajit_gcref(p) 787 | if (o == 0) { 788 | break 789 | } 790 | 791 | $*o := @cast(o, "GCobj", "$^libluajit_path") 792 | if ($*o->str->len == len) { 793 | gcs = &$*o->str 794 | s = luajit_unbox_gcstr(gcs) 795 | if (s == str) { 796 | done = 1 797 | ret = gcs 798 | break 799 | } 800 | } 801 | 802 | if (++n == strnum) { 803 | done = 1 804 | break 805 | } 806 | 807 | p = &$*o->gch->nextgc 808 | } 809 | 810 | if (done) { 811 | break 812 | } 813 | } 814 | 815 | return ret 816 | } 817 | 818 | function luajit_bucket_depth(p) 819 | { 820 | n = 0 821 | while (p) { 822 | $*o := @cast(o, "GCobj", "$^libluajit_path") 823 | o = luajit_gcref(p) 824 | if (o == 0) { 825 | break 826 | } 827 | 828 | n++; 829 | p = &$*o->gch->nextgc 830 | } 831 | return n 832 | } 833 | -------------------------------------------------------------------------------- /tapset/luajit_gc64.sxx: -------------------------------------------------------------------------------- 1 | // module luajit 2 | 3 | 4 | @use nginx.lua 5 | 6 | 7 | $*pt := @cast(pt, "GCproto", "$^libluajit_path") 8 | 9 | 10 | //@define LJ_TLIGHTUD %( 4294967292 %) 11 | @define LJ_TLIGHTUD %( 4294901760 %) 12 | @define LJ_TSTR %( 4294967291 %) 13 | @define LJ_TUDATA %( 4294967283 %) 14 | @define LJ_TTHREAD %( 4294967289 %) 15 | @define LJ_TFUNC %( 4294967287 %) 16 | 17 | 18 | @define TSTR %( 4 %) 19 | @define TUPVAL %( 5 %) 20 | @define TTHREAD %( 6 %) 21 | @define TPROTO %( 7 %) 22 | @define TFUNC %( 8 %) 23 | @define TTRACE %( 9 %) 24 | @define TCDATA %( 10 %) 25 | @define TTAB %( 11 %) 26 | @define TUDATA %( 12 %) 27 | 28 | @define CTSHIFT_NUM %( 28 %) 29 | @define CT_HASSIZE %( 5 %) 30 | @define CT_ATTRIB %( 8 %) 31 | @define CTMASK_CID %( 0xffff %) 32 | 33 | @define FF_LUA %( 0 %) 34 | @define FF_C %( 1 %) 35 | 36 | @define FRAME_LUA %( 0 %) 37 | @define FRAME_CONT %( 2 %) 38 | @define FRAME_TYPE %( 3 %) 39 | @define FRAME_P %( 4 %) 40 | @define FRAME_TYPEP %( (@FRAME_TYPE|@FRAME_P) %) 41 | 42 | @define NO_BCPOS %( ~0 %) 43 | 44 | @define LJ_GCVMASK %( 140737488355327 %) 45 | @define LJ_FR2 %( 1 %) 46 | 47 | @define sizeof_GCtab %( &@cast(0, "GCtab", "$^libluajit_path")[1] %) 48 | @define sizeof_TValue %( &@cast(0, "TValue", "$^libluajit_path")[1] %) 49 | @define sizeof_lua_State %( &@cast(0, "struct lua_State", "$^libluajit_path")[1] %) 50 | @define sizeof_GCfunc %( &@cast(0, "union GCfunc", "$^libluajit_path")[1] %) 51 | @define sizeof_GCupval %( &@cast(0, "GCupval", "$^libluajit_path")[1] %) 52 | @define sizeof_GCstr %( &@cast(0, "GCstr", "$^libluajit_path")[1] %) 53 | @define sizeof_GCudata %( &@cast(0, "GCudata", "$^libluajit_path")[1] %) 54 | @define sizeof_Node %( &@cast(0, "Node", "$^libluajit_path")[1] %) 55 | @define sizeof_GCfuncC %( &@cast(0, "GCfuncC", "$^libluajit_path")[1] %) 56 | @define sizeof_GCfuncL %( &@cast(0, "GCfuncL", "$^libluajit_path")[1] %) 57 | @define sizeof_GCRef %( &@cast(0, "GCRef", "$^libluajit_path")[1] %) 58 | @define sizeof_GCproto %( &@cast(0, "GCproto", "$^libluajit_path")[1] %) 59 | @define sizeof_GCtrace %( &@cast(0, "GCtrace", "$^libluajit_path")[1] %) 60 | @define sizeof_GCcdata %( &@cast(0, "GCcdata", "$^libluajit_path")[1] %) 61 | @define sizeof_IRIns %( &@cast(0, "IRIns", "$^libluajit_path")[1] %) 62 | @define sizeof_IRRef %( &@cast(0, "IRRef", "$^libluajit_path")[1] %) 63 | @define sizeof_SnapShot %( &@cast(0, "SnapShot", "$^libluajit_path")[1] %) 64 | @define sizeof_SnapEntry %( &@cast(0, "SnapEntry", "$^libluajit_path")[1] %) 65 | @define sizeof_K64Array %( &@cast(0, "K64Array", "$^libluajit_path")[1] %) 66 | @define sizeof_ptr %( &@cast(0, "global_State", "$^libluajit_path")->strmask %) 67 | @define sizeof_GCcdataVar %( &@cast(0, "GCcdataVar", "$^libluajit_path")[1] %) 68 | @define sizeof_BCIns %( &@cast(0, "BCIns", "$^libluajit_path")[1] %) 69 | 70 | @define strdata(s) %( 71 | (@s + @sizeof_GCstr) 72 | %) 73 | 74 | @define gcrefu(frame) %( 75 | @cast(@frame, "TValue", "$^libluajit_path")->gcr->gcptr64 76 | %) 77 | 78 | @define gcval(frame) %( 79 | (@gcrefu(@frame) & @LJ_GCVMASK) 80 | %) 81 | 82 | @define frame_gc(frame) %( 83 | @gcval(@frame - @sizeof_TValue) 84 | %) 85 | 86 | @define frame_ftsz(tv) %( 87 | @cast(@tv, "TValue", "$^libluajit_path")->ftsz 88 | %) 89 | 90 | @define frame_type(f) %( 91 | (@frame_ftsz(@f) & @FRAME_TYPE) 92 | %) 93 | 94 | @define frame_typep(f) %( 95 | (@frame_ftsz(@f) & @FRAME_TYPEP) 96 | %) 97 | 98 | @define frame_islua(f) %( 99 | (@frame_type(@f) == @FRAME_LUA) 100 | %) 101 | 102 | @define frame_iscont(f) %( 103 | (@frame_typep(@f) == @FRAME_CONT) 104 | %) 105 | 106 | @define frame_pc(tv) %( 107 | @cast(@tv, "TValue", "$^libluajit_path")->ftsz 108 | %) 109 | 110 | @define frame_contpc(f) %( 111 | (@frame_pc(@f - 2 * @sizeof_TValue)) 112 | %) 113 | 114 | @define bc_a(i) %( 115 | ((@i >> 8) & 0xff) 116 | %) 117 | 118 | @define frame_prevl(f) %( 119 | (@f - (1 + @LJ_FR2 + @bc_a(user_uint32(@frame_pc(@f) - 4))) * @sizeof_TValue) 120 | %) 121 | 122 | @define FRAME_VARG %( 3 %) 123 | 124 | @define frame_isvarg(f) %( 125 | (@frame_typep(@f) == @FRAME_VARG) 126 | %) 127 | 128 | @define frame_sized(f) %( 129 | (@frame_ftsz(@f) & ~@FRAME_TYPEP) 130 | %) 131 | 132 | @define frame_prevd(f) %( 133 | (@f - @frame_sized(@f)) 134 | %) 135 | 136 | @define isluafunc(fn) %( 137 | (@cast(@fn, "GCfunc", "$^libluajit_path")->c->ffid == @FF_LUA) 138 | %) 139 | 140 | @define isffunc(fn) %( 141 | (@cast(@fn, "GCfunc", "$^libluajit_path")->c->ffid > @FF_C) 142 | %) 143 | 144 | @define funcproto(fn) %( 145 | (@cast(@fn, "GCfunc", "$^libluajit_path")->l->pc->ptr64 - @sizeof_GCproto) 146 | %) 147 | 148 | @define proto_bc(pt) %( 149 | (@pt + @sizeof_GCproto) 150 | %) 151 | 152 | @define proto_bcpos(pt, pc) %( 153 | ((@pc - @proto_bc(@pt)) / @sizeof_TValue) 154 | %) 155 | 156 | @define proto_lineinfo(pt) %( 157 | @cast(@pt, "GCproto", "$^libluajit_path")->lineinfo->ptr64 158 | %) 159 | 160 | 161 | global luajit_cfunc_cache 162 | 163 | 164 | function luajit_frame_func(f) { 165 | gco = @frame_gc(f) 166 | $*gco := @cast(gco, "GCobj", "$^libluajit_path") 167 | return &$*gco->fn 168 | } 169 | 170 | 171 | function luajit_G(L) 172 | { 173 | $*L := @cast(L, "lua_State", "$^libluajit_path") 174 | return $*L->glref->ptr64 175 | } 176 | 177 | 178 | function luajit_gcref(r) 179 | { 180 | $*r := @cast(r, "GCRef", "$^libluajit_path") 181 | return $*r->gcptr64 182 | } 183 | 184 | 185 | function luajit_objlen(o, gct, g) 186 | { 187 | $*o := @cast(o, "GCobj", "$^libluajit_path") 188 | if (gct == @TSTR) { 189 | /* 190 | if ($*o->str->len == 0) { 191 | printf("empty string found.\n") 192 | } 193 | */ 194 | return $*o->str->len + 1 + @sizeof_GCstr 195 | } 196 | 197 | if (gct == @TTAB) { 198 | t = &$*o->tab 199 | $*t := @cast(t, "GCtab", "$^libluajit_path") 200 | asize = $*t->asize 201 | hmask = $*t->hmask 202 | 203 | n = 0 204 | if (hmask > 0) { 205 | n += @sizeof_Node * (hmask + 1) 206 | } 207 | 208 | if (asize > 0 && $*t->colo <= 0) { 209 | n += @sizeof_TValue * asize 210 | } 211 | 212 | if ($*t->colo) { 213 | n += ($*t->colo & 0x7f) * @sizeof_TValue + @sizeof_GCtab 214 | 215 | } else { 216 | n += @sizeof_GCtab 217 | } 218 | 219 | return n 220 | } 221 | 222 | if (gct == @TUDATA) { 223 | return $*o->ud->len + @sizeof_GCudata 224 | } 225 | 226 | if (gct == @TPROTO) { 227 | return $*o->pt->sizept 228 | } 229 | 230 | if (gct == @TTHREAD) { 231 | L = &$*o->th 232 | //printf("open upval: %p\n", luajit_gcref(&$*L->openupval)) 233 | //printf("lua_State: %d\n", @sizeof_lua_State) 234 | n = @sizeof_lua_State + $*L->stacksize * @sizeof_TValue 235 | p = &$*L->openupval 236 | while (p) { 237 | o = luajit_gcref(p) 238 | if (o == 0) { 239 | break; 240 | } 241 | 242 | $*o := @cast(o, "GCobj", "$^libluajit_path") 243 | gct = $*o->gch->gct 244 | size = luajit_objlen(o, gct, g) 245 | //printf("%s: %d\n", typenames[@TSTR], size) 246 | n += size 247 | p = &$*o->gch->nextgc 248 | } 249 | 250 | return n 251 | } 252 | 253 | if (gct == @TFUNC) { 254 | fn = &$*o->fn 255 | $*fn := @cast(fn, "GCfunc", "$^libluajit_path"); 256 | if (@isluafunc(fn)) { 257 | n = $*fn->l->nupvalues 258 | //n = 0 259 | return @sizeof_GCfuncL - @sizeof_GCRef + @sizeof_GCRef * n 260 | } 261 | 262 | n = $*fn->c->nupvalues 263 | //n = 0 264 | return @sizeof_GCfuncC - @sizeof_TValue + @sizeof_TValue * n 265 | } 266 | 267 | if (gct == @TUPVAL) { 268 | return @sizeof_GCupval 269 | } 270 | 271 | if (gct == @TTRACE) { 272 | T = o 273 | $*T := @cast(T, "GCtrace", "$^libluajit_path") 274 | return ((@sizeof_GCtrace + 7) & ~7) 275 | + ($*T->nins - $*T->nk) * @sizeof_IRIns 276 | + $*T->nsnap * @sizeof_SnapShot 277 | + $*T->nsnapmap * @sizeof_SnapEntry 278 | } 279 | 280 | $*g := @cast(g, "global_State", "$^libluajit_path") 281 | 282 | if (gct == @TCDATA) { 283 | cd = o 284 | $*cd := @cast(cd, "GCcdata", "$^libluajit_path") 285 | if ($*cd->marked & 0x80) { 286 | /* cdata is a vector */ 287 | cdatav = cd - @sizeof_GCcdataVar 288 | $*cdatav := @cast(cdatav, "GCcdataVar", "$^libluajit_path") 289 | return $*cdatav->len + $*cdatav->extra 290 | } 291 | 292 | /* cdata is not a vector */ 293 | cts = $*g->ctype_state->ptr64 294 | $*cts := @cast(cts, "CTState", "$^libluajit_path") 295 | id = $*cd->ctypeid 296 | ct = &$*cts->tab[id] 297 | $*ct := @cast(ct, "CType", "$^libluajit_path") 298 | while (($*ct->info >> @CTSHIFT_NUM) == @CT_ATTRIB) { 299 | //printf("XXX skipping c type attribute\n") 300 | ct = &$*cts->tab[$*ct->info & @CTMASK_CID] 301 | } 302 | 303 | if (($*ct->info >> @CTSHIFT_NUM) <= @CT_HASSIZE) { 304 | sz = $*ct->size 305 | 306 | } else { 307 | sz = @sizeof_ptr 308 | } 309 | 310 | //printf("GCcdata size: %d\n", @sizeof_GCcdata) 311 | return @sizeof_GCcdata + sz 312 | } 313 | 314 | return 0 315 | } 316 | 317 | 318 | function luajit_jit_state_size(J) 319 | { 320 | $*J := @cast(J, "jit_State", "$^libluajit_path") 321 | 322 | ir_k64_size = 0 323 | // In the newer revision of luajit, 64-bit constants are represented as an 324 | // array of TValue. Uncomment following code if the luajit being used 325 | // is using linked list to represent 64-bit constants. As the 64-bit 326 | // constants don't take lots of memory, it does not seems to be a big deal 327 | // if you don't uncomment following code for the older luajit. 328 | // 329 | /* 330 | $*k := @cast(k, "K64Array", "$^libluajit_path") 331 | for (k = $*J->k64->ptr64; k; ) { 332 | ir_k64_size += @sizeof_K64Array 333 | k = $*k->next->ptr64 334 | } 335 | if (ir_k64_size) { 336 | //printf("64-bit constants: %d\n", ir_k64_size) 337 | } 338 | */ 339 | 340 | sizesnapmap = $*J->sizesnapmap * @sizeof_SnapEntry 341 | if (sizesnapmap) { 342 | //printf("JIT snap map buffer size: %d\n", sizesnapmap) 343 | } 344 | 345 | sizesnap = $*J->sizesnap * @sizeof_SnapShot 346 | if (sizesnap) { 347 | //printf("JIT snap buffer size: %d\n", sizesnap) 348 | } 349 | 350 | sizeirbuf = ($*J->irtoplim - $*J->irbotlim) * @sizeof_IRIns 351 | if (sizeirbuf) { 352 | //printf("JIT IR buffer size: %d\n", sizeirbuf) 353 | } 354 | 355 | sizetrace = $*J->sizetrace * @sizeof_GCRef 356 | if (sizetrace) { 357 | //printf("JIT trace array size: %d\n", sizetrace) 358 | } 359 | 360 | return ir_k64_size + sizesnapmap + sizesnap + sizeirbuf + sizetrace 361 | } 362 | 363 | 364 | /* returns a TValue* pointer */ 365 | function luajit_index2adr(L, idx) 366 | { 367 | if (idx > 0) { 368 | o = $*L->base + (idx - 1) * @sizeof_TValue 369 | return o < $*L->top ? o : 0 370 | } 371 | 372 | top = $*L->top 373 | 374 | if (idx != 0 && -idx <= top - $*L->base) { 375 | return top + idx * @sizeof_TValue; 376 | } 377 | 378 | return 0 379 | } 380 | 381 | 382 | /* convert GCstr to stap string */ 383 | function luajit_unbox_gcstr(gcs) 384 | { 385 | if (gcs == 0) { 386 | return "" 387 | } 388 | 389 | $*gcs := @cast(gcs, "GCstr", "$^libluajit_path") 390 | src = @strdata(gcs) 391 | return user_string_n_warn(src, $*gcs->len) 392 | } 393 | 394 | 395 | function luajit_tostring(L, idx) 396 | { 397 | $*o := @cast(o, "TValue", "$^libluajit_path") 398 | o = luajit_index2adr(L, idx) 399 | if (o == 0) { 400 | return "" 401 | } 402 | 403 | if ($*o->it == @LJ_TSTR) { 404 | gco = $*o->gcr->gcptr64 405 | $*gco := @cast(gco, "GCobj", "$^libluajit_path") 406 | s = &$*gco->str 407 | $*s := @cast(s, "GCstr", "$^libluajit_path") 408 | return user_string_n(s + @sizeof_GCstr, $*s->len) 409 | } 410 | 411 | return "" 412 | } 413 | 414 | 415 | function luajit_tostringlen(L, idx) 416 | { 417 | $*o := @cast(o, "TValue", "$^libluajit_path") 418 | o = luajit_index2adr(L, idx) 419 | if (o == 0) { 420 | return -1 421 | } 422 | 423 | if ($*o->it == @LJ_TSTR) { 424 | gco = $*o->gcr->gcptr64 425 | $*gco := @cast(gco, "GCobj", "$^libluajit_path") 426 | s = &$*gco->str 427 | $*s := @cast(s, "GCstr", "$^libluajit_path") 428 | return $*s->len 429 | } 430 | 431 | return -1 432 | } 433 | 434 | 435 | function luajit_touserdata(L, idx) 436 | { 437 | $*o := @cast(o, "TValue", "$^libluajit_path") 438 | o = luajit_index2adr(L, idx) 439 | if (o == 0) { 440 | return 0 441 | } 442 | 443 | //printf("udata type: %d (%d)\n", $*o->it, ($*o->it >> 15)) 444 | 445 | if ($*o->it == @LJ_TUDATA) { 446 | gco = $*o->gcr->gcptr64 447 | ud = &$*gco->ud 448 | $*ud := @cast(ud, "GCudata", "$^libluajit_path") 449 | return ud + @sizeof_GCudata 450 | } 451 | 452 | if ($*o->it == @LJ_TLIGHTUD) { 453 | return $*o->u64 & 0x7fffffffffff 454 | } 455 | 456 | return 0 457 | } 458 | 459 | 460 | function luajit_proto_chunkname(pt) { 461 | gco = $*pt->chunkname->gcptr64 462 | return &$*gco->str 463 | } 464 | 465 | 466 | function luajit_debug_framepc(L, T, fn, pt, nextframe) { 467 | //printf("debug framepc: L=%p, fn=%p, nextframe=%p\n", L, fn, nextframe) 468 | if (nextframe == 0) { 469 | return @NO_BCPOS 470 | } 471 | 472 | if (@frame_islua(nextframe)) { 473 | ins = @frame_pc(nextframe); 474 | //printf("frame is lua, ins = %p\n", ins) 475 | 476 | } else if (@frame_iscont(nextframe)) { 477 | //println("frame is cont") 478 | ins = @frame_contpc(nextframe) 479 | 480 | } else { 481 | /* TODO: add support for cframe */ 482 | } 483 | 484 | //printf("debug framepc: ins = %p\n", ins) 485 | pos = @proto_bcpos(pt, ins) - 1; 486 | //printf("debug framepc: pos = %d\n", pos) 487 | sizebc = $*pt->sizebc 488 | //printf("debug framepc: ins = %p, pos=%d, sizebc=%d\n", ins, pos, sizebc) 489 | if (pos > sizebc) { 490 | //printf("Undo the effects of lj_trace_exit for JLOOP.\n") 491 | if (!T) { 492 | T = ins - 1 * @sizeof_BCIns - &@cast(0, "GCtrace", "$^libluajit_path")->startins; 493 | } 494 | //printf("startpc: %d\n", $*T->startpc->ptr64) 495 | pos = @proto_bcpos(pt, $*T->startpc->ptr64); 496 | //printf("Found adjusted position: %d\n", pos) 497 | } 498 | return pos; 499 | } 500 | 501 | 502 | function luajit_debug_line(pt, pc) 503 | { 504 | lineinfo = @proto_lineinfo(pt) 505 | //printf("lj_debug_line: lineinfo = %p, %x <= %x\\n", lineinfo, 506 | //pc, $pt->sizebc) 507 | sizebc = $*pt->sizebc 508 | 509 | if (pc <= sizebc && lineinfo) { 510 | first = $*pt->firstline 511 | 512 | if (pc == sizebc) { 513 | return first + $*pt->numline 514 | } 515 | 516 | if (pc-- == 0) { 517 | return first 518 | } 519 | 520 | if ($*pt->numline < 256) { 521 | return first + @cast(lineinfo, "uint8_t", "$^libluajit_path")[pc] 522 | } 523 | 524 | if ($*pt->numline < 65536) { 525 | return first + @cast(lineinfo, "uint16_t", "$^libluajit_path")[pc] 526 | } 527 | 528 | return first + @cast(lineinfo, "uint32_t", "$^libluajit_path")[pc] 529 | } 530 | 531 | return -1 532 | } 533 | 534 | 535 | function luajit_debug_frameline(L, T, fn, pt, nextframe) 536 | { 537 | pc = luajit_debug_framepc(L, T, fn, pt, nextframe) 538 | if (pc != @NO_BCPOS) { 539 | if (pc <= $*pt->sizebc) { 540 | return luajit_debug_line(pt, pc) 541 | } 542 | } 543 | 544 | return -1 545 | } 546 | 547 | 548 | 549 | function luajit_debug_dumpstack(L, T, depth, base, simple) 550 | { 551 | level = 0 552 | dir = 1 553 | if (depth < 0) { // reverse frames 554 | level = ~depth 555 | depth = dir = -1 556 | } 557 | bt = "" 558 | while (level != depth) { 559 | /* lj_debug_frame(L, level, &size) {{{ */ 560 | bot = $*L->stack->ptr64 + @sizeof_TValue //@LJ_FR2 561 | found_frame = 0 562 | tmp_level = level 563 | /* Traverse frames backwards. */ 564 | for (nextframe = frame = base - @sizeof_TValue; frame > bot; ) { 565 | if (@frame_gc(frame) == L) { 566 | tmp_level++ 567 | } 568 | 569 | if (tmp_level-- == 0) { 570 | size = (nextframe - frame) / @sizeof_TValue 571 | found_frame = 1 572 | break 573 | } 574 | nextframe = frame 575 | 576 | if (@frame_islua(frame)) { 577 | frame = @frame_prevl(frame) 578 | 579 | } else { 580 | if (@frame_isvarg(frame)) { 581 | tmp_level++; /* Skip vararg pseudo-frame. */ 582 | } 583 | 584 | frame = @frame_prevd(frame); 585 | } 586 | } 587 | 588 | if (!found_frame) { 589 | frame = 0 590 | size = tmp_level 591 | } 592 | /* }}} */ 593 | 594 | if (frame) { 595 | nextframe = size ? frame + size * @sizeof_TValue : 0 596 | fn = luajit_frame_func(frame) 597 | if (fn == 0) { 598 | return "" 599 | } 600 | if (@isluafunc(fn)) { 601 | pt = @funcproto(fn) 602 | 603 | if (simple) { 604 | line = $*pt->firstline 605 | 606 | } else { 607 | line = luajit_debug_frameline(L, T, fn, pt, nextframe) 608 | if (line < 0) { 609 | line = $*pt->firstline 610 | //printf("using firstline %d\n", line) 611 | 612 | } else { 613 | //printf("GOT the lineno %d\n", line) 614 | } 615 | } 616 | 617 | name = luajit_proto_chunkname(pt) /* GCstr *name */ 618 | if (name == 0) { 619 | return "" 620 | } 621 | 622 | path = luajit_unbox_gcstr(name) 623 | bt .= sprintf("%s:%d\n", path, line) 624 | 625 | } else if (@isffunc(fn)) { 626 | bt .= sprintf("builtin#%d\n", $*fn->c->ffid) 627 | 628 | } else { 629 | /* C function */ 630 | cfunc = $*fn->c->f 631 | sym = luajit_cfunc_cache[cfunc] 632 | if (sym == "") { 633 | sym = sprintf("C:%s\n", usymname(cfunc)) 634 | luajit_cfunc_cache[cfunc] = sym 635 | } 636 | bt .= sym 637 | } 638 | 639 | } else if (dir == 1) { 640 | break 641 | 642 | } else { 643 | level -= size 644 | } 645 | level += dir 646 | } 647 | 648 | return bt 649 | } 650 | 651 | 652 | function luajit_cur_thread(g) 653 | { 654 | $*g := @cast(g, "global_State", "$^libluajit_path") 655 | gco = $*g->cur_L->gcptr64 656 | if (gco == 0) { 657 | return 0 658 | } 659 | 660 | return &$*gco->th 661 | } 662 | 663 | 664 | function luajit_get_trace(g, traceno) 665 | { 666 | GG = g - &@cast(0, "GG_State", "$^libluajit_path")->g 667 | $*GG := @cast(GG, "GG_State", "$^libluajit_path") 668 | J = &$*GG->J 669 | $*J := @cast(J, "jit_State", "$^libluajit_path") 670 | if (J == 0) { 671 | return 0 672 | } 673 | 674 | return $*J->trace[traceno]->gcptr64 675 | } 676 | 677 | 678 | function luajit_trace_starting_func(g, trace) 679 | { 680 | $*trace := @cast(trace, "GCtrace", "$^libluajit_path") 681 | gco = $*trace->startpt->gcptr64 682 | if (gco == 0) { 683 | return "" 684 | } 685 | 686 | pt = &$*gco->pt 687 | 688 | firstline = $*pt->firstline 689 | 690 | name = luajit_proto_chunkname(pt) /* GCstr *name */ 691 | path = luajit_unbox_gcstr(name) 692 | 693 | return sprintf("%s:%d", path, firstline) 694 | } 695 | 696 | 697 | function luajit_print_backtrace(simple) 698 | { 699 | if (@defined(@var("globalL", "$^exec_path"))) { 700 | mL = @var("globalL", "$^exec_path") 701 | 702 | } else { 703 | mL = ngx_lua_get_main_lua_vm() 704 | } 705 | 706 | if (mL == 0) { 707 | return "" 708 | } 709 | 710 | g = luajit_G(mL) 711 | if (g == 0) { 712 | return "" 713 | } 714 | 715 | L = luajit_cur_thread(g) 716 | if (L == 0) { 717 | return "" 718 | } 719 | 720 | return luajit_backtrace(L, g, simple) 721 | } 722 | 723 | 724 | function luajit_backtrace(L, g, simple) 725 | { 726 | bt = "" 727 | 728 | vmstate = $*g->vmstate 729 | if (vmstate >= 0 || (vmstate == -3 && $*g->jit_base->ptr64)) { 730 | /* compiled Lua code */ 731 | 732 | T = luajit_get_trace(g, vmstate) 733 | if (T) { 734 | if (simple) { 735 | func = luajit_trace_starting_func(g, T) 736 | if (func != "") { 737 | bt .= sprintf("T:%s\n", func) 738 | } 739 | 740 | } else { 741 | //printf("start pc: %d\n", $*T->startpc->ptr64) 742 | } 743 | 744 | #warn(sprintf("bt: %s", bt)) 745 | } 746 | 747 | base = $*g->jit_base->ptr64 748 | if (!base) { 749 | return bt 750 | } 751 | 752 | bt .= luajit_debug_dumpstack(L, T, $^arg_depth :default(30), base, simple) 753 | /* 754 | if (bt != "") { 755 | warn(sprintf("JIT backtrace: %s\n", bt)) 756 | } 757 | */ 758 | 759 | /* 760 | if (vmstate == -3) { 761 | printf("HIT JIT GC: %s===\n", bt) 762 | } 763 | */ 764 | 765 | } else { 766 | 767 | if (vmstate == -1 && !$*L->cframe) { 768 | return "" 769 | } 770 | 771 | if (vmstate == -1 || vmstate == -2 || vmstate == -3) { 772 | base = $*L->base 773 | bt .= luajit_debug_dumpstack(L, 0, $^arg_depth, base, simple) 774 | } 775 | } 776 | 777 | return bt 778 | } 779 | 780 | 781 | function luajit_vm_state(g) 782 | { 783 | return $*g->vmstate 784 | } 785 | 786 | 787 | function luajit_find_gcstr(g, str) 788 | { 789 | len = strlen(str) 790 | strmask = $*g->strmask 791 | strnum = $*g->strnum 792 | strhash = $*g->strhash 793 | $*strhash := @cast(strhash, "GCRef", "$^libluajit_path") 794 | ret = 0 795 | 796 | n = 0 797 | done = 0 798 | for (i = 0; i <= strmask; i++) { 799 | p = &$*strhash[i] 800 | 801 | while (p) { 802 | o = luajit_gcref(p) 803 | if (o == 0) { 804 | break 805 | } 806 | 807 | $*o := @cast(o, "GCobj", "$^libluajit_path") 808 | if ($*o->str->len == len) { 809 | gcs = &$*o->str 810 | s = luajit_unbox_gcstr(gcs) 811 | if (s == str) { 812 | done = 1 813 | ret = gcs 814 | break 815 | } 816 | } 817 | 818 | if (++n == strnum) { 819 | done = 1 820 | break 821 | } 822 | 823 | p = &$*o->gch->nextgc 824 | } 825 | 826 | if (done) { 827 | break 828 | } 829 | } 830 | 831 | return ret 832 | } 833 | 834 | function luajit_bucket_depth(p) 835 | { 836 | n = 0 837 | while (p) { 838 | $*o := @cast(o, "GCobj", "$^libluajit_path") 839 | o = luajit_gcref(p) 840 | if (o == 0) { 841 | break 842 | } 843 | 844 | n++; 845 | p = &$*o->gch->nextgc 846 | } 847 | return n 848 | } 849 | --------------------------------------------------------------------------------