mallocstacks.php 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. <?php
  2. if ($argc < 2) {
  3. echo "USAGE: mallocstacks PID [NUM_STACKS=1024]\n";
  4. exit(1);
  5. }
  6. $pid = (int)$argv[1];
  7. $stacks = ($argc == 3 && is_numeric($argv[2]) && (int)$argv[2] > 0) ? $argv[2] : "1024";
  8. $bpf_text = <<<EOT
  9. #include <uapi/linux/ptrace.h>
  10. BPF_HASH(calls, int);
  11. BPF_STACK_TRACE(stack_traces, {$stacks});
  12. int alloc_enter(struct pt_regs *ctx, size_t size) {
  13. int key = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);
  14. if (key < 0)
  15. return 0;
  16. u64 zero = 0, *val;
  17. val = calls.lookup_or_try_init(&key, &zero);
  18. if (val) {
  19. (*val) += size;
  20. }
  21. return 0;
  22. };
  23. EOT;
  24. $ebpf = new Bpf(["text" => $bpf_text]);
  25. $ebpf->attach_uprobe("c", "malloc", "alloc_enter", ["pid" => $pid]);
  26. echo "Attaching to malloc in pid {$pid}, Ctrl+C to quit.\n";
  27. pcntl_signal(SIGINT, "signalHandler");
  28. pcntl_async_signals(true);
  29. # sleep until Ctrl-C
  30. while (true) {
  31. sleep(99999999);
  32. }
  33. function signalHandler($signo)
  34. {
  35. global $ebpf;
  36. global $pid;
  37. switch ($signo) {
  38. case SIGINT:
  39. $calls = $ebpf->get_table("calls");
  40. $stack_traces = $ebpf->get_table("stack_traces");
  41. $calls_vals = $calls->values();
  42. $mapped = array_map(function ($val) {
  43. return [
  44. 'stack_id' => unpack("L", $val['key'])[1],
  45. 'value' => unpack("Q", $val['value'])[1]
  46. ];
  47. }, $calls_vals);
  48. usort($mapped, function($first, $sec) {
  49. if ($first['value'] == $sec['value']) {
  50. return 0;
  51. }
  52. return ($first['value'] < $sec['value']) ? 1 : -1;
  53. });
  54. foreach ($mapped as $entry) {
  55. $stack_id = $entry['stack_id'];
  56. $value = $entry['value'];
  57. printf("%d bytes allocated at:\n", $value);
  58. if ($stack_id > 0) {
  59. $stack = $stack_traces->values($stack_id, $pid);
  60. foreach ($stack as $addr) {
  61. printf("\t%s\n", $addr);
  62. }
  63. printf(" %d\n\n", $value);
  64. }
  65. }
  66. exit(0);
  67. }
  68. }