Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37cc8984 authored by Christopher Ferris's avatar Christopher Ferris Committed by android-build-merger
Browse files

Merge "Test unwinding through a signal handler."

am: d13099e6

Change-Id: I2ff5b1dd1b15c02f50a2cb83eaf509b5f9fc4448
parents 263cf5c8 d13099e6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -114,7 +114,7 @@ cc_library_shared {
        none: true,
    },
    cflags: ["-O0"],
    srcs: ["backtrace_testlib.c"],
    srcs: ["backtrace_testlib.cpp"],

    target: {
        linux: {
+146 −7
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include <algorithm>
#include <list>
#include <memory>
#include <ostream>
#include <string>
#include <vector>

@@ -52,6 +53,7 @@

// For the THREAD_SIGNAL definition.
#include "BacktraceCurrent.h"
#include "backtrace_testlib.h"
#include "thread_utils.h"

// Number of microseconds per milliseconds.
@@ -80,13 +82,6 @@ struct dump_thread_t {
  int32_t done;
};

extern "C" {
// Prototypes for functions in the test library.
int test_level_one(int, int, int, int, void (*)(void*), void*);

int test_recursive_call(int, void (*)(void*), void*);
}

static uint64_t NanoTime() {
  struct timespec t = { 0, 0 };
  clock_gettime(CLOCK_MONOTONIC, &t);
@@ -1602,6 +1597,150 @@ TEST(libbacktrace, unwind_disallow_device_map_remote) {
  munmap(device_map, DEVICE_MAP_SIZE);
}

class ScopedSignalHandler {
 public:
  ScopedSignalHandler(int signal_number, void (*handler)(int)) : signal_number_(signal_number) {
    memset(&action_, 0, sizeof(action_));
    action_.sa_handler = handler;
    sigaction(signal_number_, &action_, &old_action_);
  }

  ScopedSignalHandler(int signal_number, void (*action)(int, siginfo_t*, void*))
      : signal_number_(signal_number) {
    memset(&action_, 0, sizeof(action_));
    action_.sa_flags = SA_SIGINFO;
    action_.sa_sigaction = action;
    sigaction(signal_number_, &action_, &old_action_);
  }

  ~ScopedSignalHandler() { sigaction(signal_number_, &old_action_, nullptr); }

 private:
  struct sigaction action_;
  struct sigaction old_action_;
  const int signal_number_;
};

static void SetValueAndLoop(void* data) {
  volatile int* value = reinterpret_cast<volatile int*>(data);

  *value = 1;
  for (volatile int i = 0;; i++)
    ;
}

static void UnwindThroughSignal(bool use_action) {
  volatile int value = 0;
  pid_t pid;
  if ((pid = fork()) == 0) {
    if (use_action) {
      ScopedSignalHandler ssh(SIGUSR1, test_signal_action);

      test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
    } else {
      ScopedSignalHandler ssh(SIGUSR1, test_signal_handler);

      test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
    }
  }
  ASSERT_NE(-1, pid);

  int read_value = 0;
  uint64_t start = NanoTime();
  while (read_value == 0) {
    usleep(1000);

    // Loop until the remote function gets into the final function.
    ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);

    WaitForStop(pid);

    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));

    size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(const_cast<int*>(&value)),
                                        reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
    ASSERT_EQ(sizeof(read_value), bytes_read);

    ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);

    ASSERT_TRUE(NanoTime() - start < 5 * NS_PER_SEC)
        << "Remote process did not execute far enough in 5 seconds.";
  }

  // Now need to send a signal to the remote process.
  kill(pid, SIGUSR1);

  // Wait for the process to get to the signal handler loop.
  Backtrace::const_iterator frame_iter;
  start = NanoTime();
  std::unique_ptr<Backtrace> backtrace;
  while (true) {
    usleep(1000);

    ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);

    WaitForStop(pid);

    backtrace.reset(Backtrace::Create(pid, pid));
    ASSERT_TRUE(backtrace->Unwind(0));
    bool found = false;
    for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
      if (frame_iter->func_name == "test_loop_forever") {
        ++frame_iter;
        found = true;
        break;
      }
    }
    if (found) {
      break;
    }

    ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);

    ASSERT_TRUE(NanoTime() - start < 5 * NS_PER_SEC)
        << "Remote process did not get in signal handler in 5 seconds." << std::endl
        << DumpFrames(backtrace.get());
  }

  std::vector<std::string> names;
  // Loop through the frames, and save the function names.
  size_t frame = 0;
  for (; frame_iter != backtrace->end(); ++frame_iter) {
    if (frame_iter->func_name == "test_level_four") {
      frame = names.size() + 1;
    }
    names.push_back(frame_iter->func_name);
  }
  ASSERT_NE(0U, frame) << "Unable to find test_level_four in backtrace" << std::endl
                       << DumpFrames(backtrace.get());

  // The expected order of the frames:
  //   test_loop_forever
  //   test_signal_handler|test_signal_action
  //   <OPTIONAL_FRAME> May or may not exist.
  //   SetValueAndLoop (but the function name might be empty)
  //   test_level_four
  //   test_level_three
  //   test_level_two
  //   test_level_one
  ASSERT_LE(frame + 2, names.size()) << DumpFrames(backtrace.get());
  ASSERT_LE(2U, frame) << DumpFrames(backtrace.get());
  if (use_action) {
    ASSERT_EQ("test_signal_action", names[0]) << DumpFrames(backtrace.get());
  } else {
    ASSERT_EQ("test_signal_handler", names[0]) << DumpFrames(backtrace.get());
  }
  ASSERT_EQ("test_level_three", names[frame]) << DumpFrames(backtrace.get());
  ASSERT_EQ("test_level_two", names[frame + 1]) << DumpFrames(backtrace.get());
  ASSERT_EQ("test_level_one", names[frame + 2]) << DumpFrames(backtrace.get());

  FinishRemoteProcess(pid);
}

TEST(libbacktrace, unwind_remote_through_signal_using_handler) { UnwindThroughSignal(false); }

TEST(libbacktrace, unwind_remote_through_signal_using_action) { UnwindThroughSignal(true); }

#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"

+23 −13
Original line number Diff line number Diff line
@@ -15,31 +15,41 @@
 */

#include <libunwind.h>
#include <signal.h>
#include <stdio.h>

int test_level_four(int one, int two, int three, int four,
                    void (*callback_func)(void*), void* data) {
#include "backtrace_testlib.h"

void test_loop_forever() {
  while (1)
    ;
}

void test_signal_handler(int) { test_loop_forever(); }

void test_signal_action(int, siginfo_t*, void*) { test_loop_forever(); }

int test_level_four(int one, int two, int three, int four, void (*callback_func)(void*),
                    void* data) {
  if (callback_func != NULL) {
    callback_func(data);
  } else {
    while (1) {
    }
    while (1)
      ;
  }
  return one + two + three + four;
}

int test_level_three(int one, int two, int three, int four,
                     void (*callback_func)(void*), void* data) {
int test_level_three(int one, int two, int three, int four, void (*callback_func)(void*),
                     void* data) {
  return test_level_four(one + 3, two + 6, three + 9, four + 12, callback_func, data) + 3;
}

int test_level_two(int one, int two, int three, int four,
                   void (*callback_func)(void*), void* data) {
int test_level_two(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
  return test_level_three(one + 2, two + 4, three + 6, four + 8, callback_func, data) + 2;
}

int test_level_one(int one, int two, int three, int four,
                   void (*callback_func)(void*), void* data) {
int test_level_one(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
  return test_level_two(one + 1, two + 2, three + 3, four + 4, callback_func, data) + 1;
}

+38 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _LIBBACKTRACE_BACKTRACE_TESTLIB_H
#define _LIBBACKTRACE_BACKTRACE_TESTLIB_H

#include <sys/cdefs.h>

#include <libunwind.h>

__BEGIN_DECLS

void test_loop_forever();
void test_signal_handler(int);
void test_signal_action(int, siginfo_t*, void*);
int test_level_four(int, int, int, int, void (*)(void*), void*);
int test_level_three(int, int, int, int, void (*)(void*), void*);
int test_level_two(int, int, int, int, void (*)(void*), void*);
int test_level_one(int, int, int, int, void (*)(void*), void*);
int test_recursive_call(int, void (*)(void*), void*);
void test_get_context_and_wait(unw_context_t*, volatile int*);

__END_DECLS

#endif  // _LIBBACKTRACE_BACKTRACE_TESTLIB_H