global jmp_buf, jmp_target, jmp_backtrace

function callers:string()
{
  bt = ubacktrace();
  // Skip the first caller, which is setjmp or longjmp itself.
  callers = "";
  token = tokenize(bt, " ");
  while ((token = tokenize("", " ")) != "")
    callers = callers . " " . token;
  return callers;
}

function nth_caller:long(n, bt)
{
  token = tokenize(bt, " ");
  while (n-- > 0)
    token = tokenize("", " ");
  return strtol(token, 16);
}

global started = 0
probe process(@1).function("main")
{
  started = 1;
}

probe process("/lib*/libc.so.*").mark("setjmp")
{
  if (!started) next;

  jmp_buf[tid()] = $arg1;
  jmp_target[tid()] = $arg3;
  bt = jmp_backtrace[tid()] = callers();
  for (i = 0; i < 5; ++i)
    if (usymname(nth_caller(i, bt)) == "setter")
      next;
  printf("setjmp backtrace doesn't include setter:\n");
  print_ubacktrace();
}

function check_jmp(buf, target)
{
  if (jmp_buf[tid()] != buf)
    printf("longjmp env %p, expected %p\n", buf, jmp_buf[tid()]);
  if (jmp_target[tid()] != target)
    printf("longjmp target %p, expected %p\n", target, jmp_target[tid()]);
}

probe process("/lib*/libc.so.*").mark("longjmp")
{
  if (!started) next;

  check_jmp($arg1, $arg3);
  bt = callers();
  if (bt == jmp_backtrace[tid()])
    printf("longjmp backtrace matches setjmp backtrace!\n");
  for (i = 0; i < 5; ++i)
    if (usymname(nth_caller(i, bt)) == "jumper")
      next;
  printf("longjmp backtrace doesn't include jumper:\n");
  print_ubacktrace();
}

probe process("/lib*/libc.so.*").mark("longjmp_target")
{
  if (!started) next;

  check_jmp($arg1, $arg3);
  bt = callers();
  if (bt != jmp_backtrace[tid()])
    printf("longjmp_target backtrace does not match setjmp:\n\t%s\nvs\t%s",
	   sprint_usyms(jmp_backtrace[tid()]), sprint_usyms(bt));

  printf("hit longjmp_target\n");
}
