TEST(libbacktrace, thread_ignore_frames) {
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  thread_t thread_data = { 0, 0, 0 };
  pthread_t thread;
  ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);

  // Wait up to 2 seconds for the tid to be set.
  ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));

  UniquePtr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
  ASSERT_TRUE(all.get() != NULL);
  ASSERT_TRUE(all->Unwind(0));

  UniquePtr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
  ASSERT_TRUE(ign1.get() != NULL);
  ASSERT_TRUE(ign1->Unwind(1));

  UniquePtr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
  ASSERT_TRUE(ign2.get() != NULL);
  ASSERT_TRUE(ign2->Unwind(2));

  VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), NULL);

  // Tell the thread to exit its infinite loop.
  android_atomic_acquire_store(0, &thread_data.state);
}
TEST(libbacktrace, thread_ignore_frames) {
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  thread_t thread_data = { 0, 0, 0 };
  pthread_t thread;
  ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);

  // Wait up to 2 seconds for the tid to be set.
  ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));

  backtrace_context_t all;
  ASSERT_TRUE(backtrace_create_context(&all, getpid(), thread_data.tid, 0));

  backtrace_context_t ign1;
  ASSERT_TRUE(backtrace_create_context(&ign1, getpid(), thread_data.tid, 1));

  backtrace_context_t ign2;
  ASSERT_TRUE(backtrace_create_context(&ign2, getpid(), thread_data.tid, 2));

  VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace, NULL);

  backtrace_destroy_context(&all);
  backtrace_destroy_context(&ign1);
  backtrace_destroy_context(&ign2);

  // Tell the thread to exit its infinite loop.
  android_atomic_acquire_store(0, &thread_data.state);
}
void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
  UniquePtr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
  ASSERT_TRUE(ign1.get() != NULL);
  ASSERT_TRUE(ign1->Unwind(1));

  UniquePtr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
  ASSERT_TRUE(ign2.get() != NULL);
  ASSERT_TRUE(ign2->Unwind(2));

  VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), NULL);
}
void VerifyProcessIgnoreFrames(const backtrace_t* bt_all) {
  pid_t pid = bt_all->pid;

  backtrace_context_t ign1;
  ASSERT_TRUE(backtrace_create_context(&ign1, pid, -1, 1));
  ASSERT_TRUE(ign1.backtrace != NULL);

  backtrace_context_t ign2;
  ASSERT_TRUE(backtrace_create_context(&ign2, pid, -1, 2));
  ASSERT_TRUE(ign2.backtrace != NULL);

  VerifyIgnoreFrames(bt_all, ign1.backtrace, ign2.backtrace, NULL);

  backtrace_destroy_context(&ign1);
  backtrace_destroy_context(&ign2);
}
void VerifyLevelIgnoreFrames(void*) {
  UniquePtr<Backtrace> all(
      Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
  ASSERT_TRUE(all.get() != NULL);
  ASSERT_TRUE(all->Unwind(0));

  UniquePtr<Backtrace> ign1(
      Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
  ASSERT_TRUE(ign1.get() != NULL);
  ASSERT_TRUE(ign1->Unwind(1));

  UniquePtr<Backtrace> ign2(
      Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
  ASSERT_TRUE(ign2.get() != NULL);
  ASSERT_TRUE(ign2->Unwind(2));

  VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
}
void VerifyLevelIgnoreFrames(void*) {
  backtrace_context_t all;
  ASSERT_TRUE(backtrace_create_context(&all, -1, -1, 0));
  ASSERT_TRUE(all.backtrace != NULL);

  backtrace_context_t ign1;
  ASSERT_TRUE(backtrace_create_context(&ign1, -1, -1, 1));
  ASSERT_TRUE(ign1.backtrace != NULL);

  backtrace_context_t ign2;
  ASSERT_TRUE(backtrace_create_context(&ign2, -1, -1, 2));
  ASSERT_TRUE(ign2.backtrace != NULL);

  VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace,
                     "VerifyLevelIgnoreFrames");

  backtrace_destroy_context(&all);
  backtrace_destroy_context(&ign1);
  backtrace_destroy_context(&ign2);
}