Print the backtrace of running process 33/317133/9
authorHwankyu Jhun <h.jhun@samsung.com>
Thu, 26 Dec 2024 00:02:32 +0000 (09:02 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Thu, 26 Dec 2024 08:50:17 +0000 (17:50 +0900)
This patch add an internal function to print backtrace of the running process.

Change-Id: If5fd06b8ceaae8e3607ed74c9318081b0b94b2e9
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
CMakeLists.txt
packaging/aul.spec
src/aul/CMakeLists.txt
src/aul/aul_backtrace.cc [new file with mode: 0644]
src/aul/include/aul_backtrace.h [new file with mode: 0644]
src/tool/aul_test/tests/aul_backtrace_print_test.cc [new file with mode: 0644]

index 16caf50bb7b2855da501c11f489519a9805ea3ec..5fefffcdbc0d9f75aee0713318f78c95dd5d8a2c 100644 (file)
@@ -62,6 +62,8 @@ PKG_CHECK_MODULES(TTRACE_DEPS REQUIRED ttrace)
 PKG_CHECK_MODULES(UUID_DEPS REQUIRED uuid)
 PKG_CHECK_MODULES(VCONF_DEPS REQUIRED vconf)
 PKG_CHECK_MODULES(XDGMIME_DEPS REQUIRED xdgmime)
+PKG_CHECK_MODULES(LIBUNWIND_DEPS REQUIRED libunwind)
+PKG_CHECK_MODULES(LIBUNWIND_PTRACE_DEPS REQUIRED libunwind-ptrace)
 
 # pkgconfig file
 CONFIGURE_FILE(feature/preexec_list.txt.in
index 048b561c8b3ed02daf5b30145186ba878817e766..ac689a674655230323fae4642ed0eb7e1378ee9b 100644 (file)
@@ -36,6 +36,8 @@ BuildRequires:  pkgconfig(ttrace)
 BuildRequires:  pkgconfig(uuid)
 BuildRequires:  pkgconfig(vconf)
 BuildRequires:  xdgmime-devel, pkgconfig(xdgmime)
+BuildRequires:  pkgconfig(libunwind)
+BuildRequires:  pkgconfig(libunwind-ptrace)
 
 Requires: libstorage
 
index cc7030cab6ddac0d4f64edf24e484a52b4e80cac..78ee6039ab39408b0b2d84185ea82d97e6e1d505 100644 (file)
@@ -53,6 +53,8 @@ APPLY_PKG_CONFIG(${TARGET_AUL} PUBLIC
   UUID_DEPS
   VCONF_DEPS
   XDGMIME_DEPS
+  LIBUNWIND_DEPS
+  LIBUNWIND_PTRACE_DEPS
 )
 
 TARGET_LINK_LIBRARIES(${TARGET_AUL} PRIVATE ${TARGET_AUL_BLINK})
diff --git a/src/aul/aul_backtrace.cc b/src/aul/aul_backtrace.cc
new file mode 100644 (file)
index 0000000..847386f
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ */
+
+#include "aul/include/aul_backtrace.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+#include <linux/limits.h>
+
+#include <string>
+
+#include "aul/aul_api.h"
+#include "aul/common/log_private.hh"
+#include "aul/include/aul.h"
+
+namespace {
+
+class Backtrace {
+ public:
+  Backtrace(pid_t pid) : pid_(pid) {
+    unw_addr_space_t addr_space = unw_create_addr_space(&_UPT_accessors, 0);
+    if (!addr_space) {
+      _E("Failed to create address space");
+      return;
+    }
+
+    struct UPT_info* upt_info =
+        static_cast<struct UPT_info*>(_UPT_create(pid_));
+    if (!upt_info) {
+      _E("Failed to create UPT_info");
+      unw_destroy_addr_space(addr_space);
+      return;
+    }
+
+    addr_space_ = addr_space;
+    upt_info_ = upt_info;
+  }
+
+  ~Backtrace() {
+    if (upt_info_) _UPT_destroy(upt_info_);
+    if (addr_space_) unw_destroy_addr_space(addr_space_);
+  }
+
+  void Print() {
+    if (!upt_info_ || !addr_space_) return;
+
+    unw_cursor_t cursor;
+    if (unw_init_remote(&cursor, addr_space_, upt_info_) < 0) {
+      _E("Failed to initialize unwinding");
+      return;
+    }
+
+    fprintf(stderr, "[pid %d][Backtrace]\n", pid_);
+    uint32_t index = 0;
+    Print(cursor, index++);
+
+    while (unw_step(&cursor) > 0) Print(cursor, index++);
+
+    fflush(stderr);
+  }
+
+ private:
+  void Print(unw_cursor_t cursor, uint32_t index) {
+    char func_name[256];
+    unw_word_t pc;
+    unw_get_reg(&cursor, UNW_REG_IP, &pc);
+
+    unw_word_t offset;
+    if (!unw_get_proc_name(&cursor, func_name, sizeof(func_name), &offset)) {
+      fprintf(stderr, "[pid %d][%03u] 0x%lx: %s + 0x%lx [%s]\n", pid_, index, pc,
+                    func_name, offset, GetModuleName(pc));
+    } else {
+      fprintf(stderr, "[pid %d][%03u] 0x%lx: Unknown\n", pid_, index, pc);
+    }
+  }
+
+  const char* GetModuleName(unsigned long addr) {
+    char line[PATH_MAX];
+    std::string maps_path = "/proc/" + std::to_string(pid_) + "/maps";
+    FILE* maps_file = fopen(maps_path.c_str(), "r");
+    if (!maps_file) {
+      _E("Failed to open maps file. pid=%d", pid_);
+      return "Unknown";
+    }
+
+    unsigned long start;
+    unsigned long end;
+    char perms[5];
+    char dev[6];
+    char path[PATH_MAX];
+    int inode;
+    while (fgets(line, sizeof(line), maps_file)) {
+      if (sscanf(line, "%lx-%lx %4s %*x %5s %d %s", &start, &end, perms, dev,
+                 &inode, path) >= 6) {
+        if (addr >= start && addr < end) {
+          fclose(maps_file);
+          strncpy(module_name_, path, sizeof(module_name_));
+          return module_name_;
+        }
+      }
+    }
+
+    fclose(maps_file);
+    return "Unknown";
+  }
+
+ private:
+  pid_t pid_;
+  unw_addr_space_t addr_space_ = nullptr;
+  struct UPT_info* upt_info_ = nullptr;
+  char module_name_[PATH_MAX] = { 0, };
+};
+
+class Ptrace {
+ public:
+  Ptrace(pid_t pid) : pid_(pid) {}
+
+  ~Ptrace() { Detach(); }
+
+  int Attach() {
+    if (ptrace(PTRACE_ATTACH, pid_, NULL, NULL) == -1) {
+      _E("Failed to attach to process. pid=%d, errno=%d", pid_, errno);
+      return -1;
+    }
+
+    attached_ = true;
+    return 0;
+  }
+
+  bool WaitPid() {
+    int status;
+    waitpid(pid_, &status, 0);
+    if (WIFSTOPPED(status)) return true;
+
+    return false;
+  }
+
+  void PrintBacktrace() {
+    Backtrace backtrace(pid_);
+    backtrace.Print();
+  }
+
+ private:
+  void Detach() {
+    if (!attached_) return;
+
+    if (ptrace(PTRACE_DETACH, pid_, NULL, NULL) == -1) perror("ptrace DETACH");
+
+    attached_ = false;
+  }
+
+ private:
+  bool attached_ = false;
+  pid_t pid_;
+};
+
+}  // namespace
+
+extern "C" API int aul_backtrace_print(pid_t pid) {
+  if (pid < 1) {
+    _E("Invalid parameter");
+    return AUL_R_EINVAL;
+  }
+
+  Ptrace trace(pid);
+  if (trace.Attach() != 0) return AUL_R_ERROR;
+  if (!trace.WaitPid()) return AUL_R_ERROR;
+
+  trace.PrintBacktrace();
+  return AUL_R_OK;
+}
diff --git a/src/aul/include/aul_backtrace.h b/src/aul/include/aul_backtrace.h
new file mode 100644 (file)
index 0000000..839f138
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __AUL_BACKTRACE_H__
+#define __AUL_BACKTRACE_H__
+
+#include <sys/types.h>
+
+#include <aul.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Prints backtrace information of the specified process.
+ * @param[in] pid The process ID.
+ * @return @c 0 on success, otherwise a negative error value
+ * @retval #AUL_R_OK Successful
+ * @retval #AUL_R_EINVAL Invalid parameter
+ * @retval #AUL_R_ERROR General error
+ */
+int aul_backtrace_print(pid_t pid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AUL_BACKTRACE_H__ */
diff --git a/src/tool/aul_test/tests/aul_backtrace_print_test.cc b/src/tool/aul_test/tests/aul_backtrace_print_test.cc
new file mode 100644 (file)
index 0000000..04ad626
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#include "include/aul_backtrace.h"
+
+#include <stdio.h>
+
+#include "aul_test.hh"
+#include "log_private.hh"
+
+namespace aul_test {
+
+class AulBacktracePrintTest : public AulTest {
+ public:
+  AulBacktracePrintTest()
+      : AulTest("bt", "aul_backtrace_print", "bt <pid>") {}
+
+  virtual ~AulBacktracePrintTest() {}
+
+  void SetUp() override {}
+
+  void TearDown() override {}
+
+  int Test(int argc, char** argv) override {
+    if (argc < 3) {
+      Usage();
+      return -1;
+    }
+
+    if (getuid() != 0) {
+      fprintf(stderr, "Permission denied\n");
+      return -EPERM;
+    }
+
+    _D("[aul_backtrace_print test] %s", argv[2]);
+    return aul_backtrace_print(atoi(argv[2]));
+  }
+};
+
+AUL_TEST_REGISTER(AulBacktracePrintTest, bt_test);
+
+}  // namespace aul_test