2
0

userfaultfd-wrlat.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. #!/usr/bin/python3
  2. #
  3. # userfaultfd-wrlat Summarize userfaultfd write fault latencies.
  4. # Events are continuously accumulated for the
  5. # run, while latency distribution histogram is
  6. # dumped each 'interval' seconds.
  7. #
  8. # For Linux, uses BCC, eBPF.
  9. #
  10. # USAGE: userfaultfd-lat [interval [count]]
  11. #
  12. # Copyright Virtuozzo GmbH, 2020
  13. #
  14. # Authors:
  15. # Andrey Gruzdev <andrey.gruzdev@virtuozzo.com>
  16. #
  17. # This work is licensed under the terms of the GNU GPL, version 2 or
  18. # later. See the COPYING file in the top-level directory.
  19. from __future__ import print_function
  20. from bcc import BPF
  21. from ctypes import c_ushort, c_int, c_ulonglong
  22. from time import sleep
  23. from sys import argv
  24. def usage():
  25. print("USAGE: %s [interval [count]]" % argv[0])
  26. exit()
  27. # define BPF program
  28. bpf_text = """
  29. #include <uapi/linux/ptrace.h>
  30. #include <linux/mm.h>
  31. BPF_HASH(ev_start, u32, u64);
  32. BPF_HISTOGRAM(ev_delta_hist, u64);
  33. /* Trace UFFD page fault start event. */
  34. static void do_event_start()
  35. {
  36. /* Using "(u32)" to drop group ID which is upper 32 bits */
  37. u32 tid = (u32) bpf_get_current_pid_tgid();
  38. u64 ts = bpf_ktime_get_ns();
  39. ev_start.update(&tid, &ts);
  40. }
  41. /* Trace UFFD page fault end event. */
  42. static void do_event_end()
  43. {
  44. /* Using "(u32)" to drop group ID which is upper 32 bits */
  45. u32 tid = (u32) bpf_get_current_pid_tgid();
  46. u64 ts = bpf_ktime_get_ns();
  47. u64 *tsp;
  48. tsp = ev_start.lookup(&tid);
  49. if (tsp) {
  50. u64 delta = ts - (*tsp);
  51. /* Transform time delta to milliseconds */
  52. ev_delta_hist.increment(bpf_log2l(delta / 1000000));
  53. ev_start.delete(&tid);
  54. }
  55. }
  56. /* KPROBE for handle_userfault(). */
  57. int probe_handle_userfault(struct pt_regs *ctx, struct vm_fault *vmf,
  58. unsigned long reason)
  59. {
  60. /* Trace only UFFD write faults. */
  61. if (reason & VM_UFFD_WP) {
  62. do_event_start();
  63. }
  64. return 0;
  65. }
  66. /* KRETPROBE for handle_userfault(). */
  67. int retprobe_handle_userfault(struct pt_regs *ctx)
  68. {
  69. do_event_end();
  70. return 0;
  71. }
  72. """
  73. # arguments
  74. interval = 10
  75. count = -1
  76. if len(argv) > 1:
  77. try:
  78. interval = int(argv[1])
  79. if interval == 0:
  80. raise
  81. if len(argv) > 2:
  82. count = int(argv[2])
  83. except: # also catches -h, --help
  84. usage()
  85. # load BPF program
  86. b = BPF(text=bpf_text)
  87. # attach KRPOBEs
  88. b.attach_kprobe(event="handle_userfault", fn_name="probe_handle_userfault")
  89. b.attach_kretprobe(event="handle_userfault", fn_name="retprobe_handle_userfault")
  90. # header
  91. print("Tracing UFFD-WP write fault latency... Hit Ctrl-C to end.")
  92. # output
  93. loop = 0
  94. do_exit = 0
  95. while (1):
  96. if count > 0:
  97. loop += 1
  98. if loop > count:
  99. exit()
  100. try:
  101. sleep(interval)
  102. except KeyboardInterrupt:
  103. pass; do_exit = 1
  104. print()
  105. b["ev_delta_hist"].print_log2_hist("msecs")
  106. if do_exit:
  107. exit()