text: #!/usr/bin/python
  # @lint-avoid-python-3-compatibility-imports
  #
  # filelife    Trace the lifespan of short-lived files.
  #             For Linux, uses BCC, eBPF. Embedded C.
  #
  # This traces the creation and deletion of files, providing information
  # on who deleted the file, the file age, and the file name. The intent is to
  # provide information on short-lived files, for debugging or performance
  # analysis.
  #
  # USAGE: filelife [-h] [-p PID]
  #
  # Copyright 2016 Netflix, Inc.
  # Licensed under the Apache License, Version 2.0 (the "License")
  #
  # 08-Feb-2015   Brendan Gregg   Created this.
  # 17-Feb-2016   Allan McAleavy updated for BPF_PERF_OUTPUT
  
  from __future__ import print_function
  from bcc import BPF
  import argparse
  from time import strftime
  
  # arguments
  examples = """examples:
      ./filelife           # trace all stat() syscalls
      ./filelife -p 181    # only trace PID 181
  """
  parser = argparse.ArgumentParser(
      description="Trace stat() syscalls",
      formatter_class=argparse.RawDescriptionHelpFormatter,
      epilog=examples)
  parser.add_argument("-p", "--pid",
      help="trace this PID only")
  parser.add_argument("--ebpf", action="store_true",
      help=argparse.SUPPRESS)
  args = parser.parse_args()
  debug = 0
  
  # define BPF program
  bpf_text = """
  #include <uapi/linux/ptrace.h>
  #include <linux/fs.h>
  #include <linux/sched.h>
  
  struct data_t {
      u32 pid;
      u64 delta;
      char comm[TASK_COMM_LEN];
      char fname[DNAME_INLINE_LEN];
  };
  
  BPF_HASH(birth, struct dentry *);
  BPF_PERF_OUTPUT(events);
  
  // trace file creation time
  int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
  {
      u32 pid = bpf_get_current_pid_tgid();
      FILTER
  
      u64 ts = bpf_ktime_get_ns();
      birth.update(&dentry, &ts);
  
      return 0;
  };
  
  // trace file deletion and output details
  int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry)
  {
      struct data_t data = {};
      u32 pid = bpf_get_current_pid_tgid();
  
      FILTER
  
      u64 *tsp, delta;
      tsp = birth.lookup(&dentry);
      if (tsp == 0) {
          return 0;   // missed create
      }
  
      delta = (bpf_ktime_get_ns() - *tsp) / 1000000;
      birth.delete(&dentry);
  
      struct qstr d_name = dentry->d_name;
      if (d_name.len == 0)
          return 0;
  
      if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) {
          data.pid = pid;
          data.delta = delta;
          bpf_probe_read(&data.fname, sizeof(data.fname), d_name.name);
      }
  
      events.perf_submit(ctx, &data, sizeof(data));
  
      return 0;
  }
  """
  
  if args.pid:
      bpf_text = bpf_text.replace('FILTER',
          'if (pid != %s) { return 0; }' % args.pid)
  else:
      bpf_text = bpf_text.replace('FILTER', '')
  if debug or args.ebpf:
      print(bpf_text)
      if args.ebpf:
          exit()
  
  # initialize BPF
  b = BPF(text=bpf_text)
  b.attach_kprobe(event="vfs_create", fn_name="trace_create")
  # newer kernels (say, 4.8) may don't fire vfs_create, so record (or overwrite)
  # the timestamp in security_inode_create():
  b.attach_kprobe(event="security_inode_create", fn_name="trace_create")
  b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink")
  
  # header
  print("%-8s %-6s %-16s %-7s %s" % ("TIME", "PID", "COMM", "AGE(s)", "FILE"))
  
  # process event
  def print_event(cpu, data, size):
      event = b["events"].event(data)
      print("%-8s %-6d %-16s %-7.2f %s" % (strftime("%H:%M:%S"), event.pid,
          event.comm.decode('utf-8', 'replace'), float(event.delta) / 1000,
          event.fname.decode('utf-8', 'replace')))
  
  b["events"].open_perf_buffer(print_event)
  while 1:
      try:
          b.perf_buffer_poll()
      except KeyboardInterrupt:
          exit()
  