This is an automated email from the ASF dual-hosted git repository.
dmeden pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 9d9e72bf34 trafic_crashlog: Moving the removed ATS backtrace logs from
traffic_manager to traffic_crashlog (#10811)
9d9e72bf34 is described below
commit 9d9e72bf3424a7f5c8feaf7da2f27ac58ab395ad
Author: Damian Meden <[email protected]>
AuthorDate: Tue Dec 5 18:02:31 2023 +0100
trafic_crashlog: Moving the removed ATS backtrace logs from traffic_manager
to traffic_crashlog (#10811)
---
cmake/Findunwind.cmake | 7 +-
src/traffic_crashlog/CMakeLists.txt | 6 +-
src/traffic_crashlog/backtrace.cc | 208 +++++++++++++++++++++++++++++++
src/traffic_crashlog/traffic_crashlog.cc | 60 +++------
4 files changed, 235 insertions(+), 46 deletions(-)
diff --git a/cmake/Findunwind.cmake b/cmake/Findunwind.cmake
index fd77524c6f..b192ccde8f 100644
--- a/cmake/Findunwind.cmake
+++ b/cmake/Findunwind.cmake
@@ -28,8 +28,9 @@
# unwind::unwind
#
-find_library(unwind_LIBRARY NAMES unwind)
-find_path(unwind_INCLUDE_DIR NAMES libunwind.h libunwind/libunwind.h)
+find_library(unwind_ptrace_LIBRARY NAMES unwind-ptrace)
+find_library(unwind_generic_LIBRARY NAMES unwind-generic)
+find_path(unwind_INCLUDE_DIR NAMES libunwind.h)
mark_as_advanced(unwind_FOUND unwind_LIBRARY unwind_INCLUDE_DIR)
@@ -43,5 +44,5 @@ endif()
if(unwind_FOUND AND NOT TARGET unwind::unwind)
add_library(unwind::unwind INTERFACE IMPORTED)
target_include_directories(unwind::unwind INTERFACE ${unwind_INCLUDE_DIRS})
- target_link_libraries(unwind::unwind INTERFACE "${unwind_LIBRARY}")
+ target_link_libraries(unwind::unwind INTERFACE "${unwind_ptrace_LIBRARY}"
"${unwind_generic_LIBRARY}")
endif()
diff --git a/src/traffic_crashlog/CMakeLists.txt
b/src/traffic_crashlog/CMakeLists.txt
index e78cb924df..5d98d633cc 100644
--- a/src/traffic_crashlog/CMakeLists.txt
+++ b/src/traffic_crashlog/CMakeLists.txt
@@ -15,8 +15,12 @@
#
#######################
-add_executable(traffic_crashlog procinfo.cc traffic_crashlog.cc)
+add_executable(traffic_crashlog procinfo.cc backtrace.cc traffic_crashlog.cc)
target_link_libraries(traffic_crashlog PRIVATE ts::inkevent ts::records
ts::tscore ts::tsapicore)
+if(TS_USE_REMOTE_UNWINDING)
+ target_link_libraries(traffic_crashlog PRIVATE unwind::unwind)
+endif()
+
install(TARGETS traffic_crashlog)
diff --git a/src/traffic_crashlog/backtrace.cc
b/src/traffic_crashlog/backtrace.cc
new file mode 100644
index 0000000000..ed9fbc503d
--- /dev/null
+++ b/src/traffic_crashlog/backtrace.cc
@@ -0,0 +1,208 @@
+/** @file
+
+ backtrace.cc
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+ */
+
+/****************************************************************************
+
+ backtrace.cc
+
+ Functions to generate backtrace from a TS process.
+
+ ****************************************************************************/
+#include "tscore/ink_config.h"
+
+#if TS_USE_REMOTE_UNWINDING
+#include "tscore/Diags.h"
+
+#include "tscore/TextBuffer.h"
+#include <string.h>
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+#include <sys/ptrace.h>
+#include <cxxabi.h>
+#include <vector>
+
+namespace
+{
+using threadlist = std::vector<pid_t>;
+
+static threadlist
+threads_for_process(pid_t proc)
+{
+ DIR *dir = nullptr;
+ struct dirent *entry = nullptr;
+
+ char path[64];
+ threadlist threads;
+
+ if (snprintf(path, sizeof(path), "/proc/%ld/task", static_cast<long>(proc))
>= static_cast<int>(sizeof(path))) {
+ goto done;
+ }
+
+ dir = opendir(path);
+ if (dir == nullptr) {
+ goto done;
+ }
+
+ while ((entry = readdir(dir))) {
+ pid_t threadid;
+
+ if (isdot(entry->d_name) || isdotdot(entry->d_name)) {
+ continue;
+ }
+
+ threadid = strtol(entry->d_name, nullptr, 10);
+ if (threadid > 0) {
+ threads.push_back(threadid);
+ Debug("backtrace", "found thread %ld\n", (long)threadid);
+ }
+ }
+
+done:
+ if (dir) {
+ closedir(dir);
+ }
+
+ return threads;
+}
+
+static void
+backtrace_for_thread(pid_t threadid, TextBuffer &text)
+{
+ int status;
+ unw_addr_space_t addr_space = nullptr;
+ unw_cursor_t cursor;
+ void *ap = nullptr;
+ pid_t target = -1;
+ unsigned level = 0;
+
+ // First, attach to the child, causing it to stop.
+ status = ptrace(PTRACE_ATTACH, threadid, 0, 0);
+ if (status < 0) {
+ Debug("backtrace", "ptrace(ATTACH, %ld) -> %s (%d)\n", (long)threadid,
strerror(errno), errno);
+ return;
+ }
+
+ // Wait for it to stop (XXX should be a timed wait ...)
+ target = waitpid(threadid, &status, __WALL | WUNTRACED);
+ Debug("backtrace", "waited for target %ld, found PID %ld, %s\n",
(long)threadid, (long)target,
+ WIFSTOPPED(status) ? "STOPPED" : "???");
+ if (target < 0) {
+ goto done;
+ }
+
+ ap = _UPT_create(threadid);
+ Debug("backtrace", "created UPT %p", ap);
+ if (ap == nullptr) {
+ goto done;
+ }
+
+ addr_space = unw_create_addr_space(&_UPT_accessors, 0 /* byteorder */);
+ Debug("backtrace", "created address space %p\n", addr_space);
+ if (addr_space == nullptr) {
+ goto done;
+ }
+
+ status = unw_init_remote(&cursor, addr_space, ap);
+ Debug("backtrace", "unw_init_remote(...) -> %d\n", status);
+ if (status != 0) {
+ goto done;
+ }
+
+ while (unw_step(&cursor) > 0) {
+ unw_word_t ip;
+ unw_word_t offset;
+ char buf[256];
+
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+
+ if (unw_get_proc_name(&cursor, buf, sizeof(buf), &offset) == 0) {
+ int status;
+ char *name = abi::__cxa_demangle(buf, nullptr, nullptr, &status);
+ text.format("%-4u 0x%016llx %s + %p\n", level, static_cast<unsigned long
long>(ip), name ? name : buf, (void *)offset);
+ free(name);
+ } else {
+ text.format("%-4u 0x%016llx 0x0 + %p\n", level, static_cast<unsigned
long long>(ip), (void *)offset);
+ }
+
+ ++level;
+ }
+
+done:
+ if (addr_space) {
+ unw_destroy_addr_space(addr_space);
+ }
+
+ if (ap) {
+ _UPT_destroy(ap);
+ }
+
+ status = ptrace(PTRACE_DETACH, target, NULL, NULL);
+ Debug("backtrace", "ptrace(DETACH, %ld) -> %d (errno %d)\n", (long)target,
status, errno);
+}
+} // namespace
+int
+ServerBacktrace(unsigned /* options */, int pid, char **trace)
+{
+ *trace = nullptr;
+
+ threadlist threads(threads_for_process(pid));
+ TextBuffer text(0);
+
+ Debug("backtrace", "tracing %zd threads for traffic_server PID %ld\n",
threads.size(), (long)pid);
+
+ for (auto threadid : threads) {
+ Debug("backtrace", "tracing thread %ld\n", (long)threadid);
+ // Get the thread name using /proc/PID/comm
+ ats_scoped_fd fd;
+ char threadname[128];
+
+ snprintf(threadname, sizeof(threadname), "/proc/%ld/comm",
static_cast<long>(threadid));
+ fd = open(threadname, O_RDONLY);
+ if (fd >= 0) {
+ text.format("Thread %ld, ", static_cast<long>(threadid));
+ text.readFromFD(fd);
+ text.chomp();
+ } else {
+ text.format("Thread %ld", static_cast<long>(threadid));
+ }
+
+ text.format(":\n");
+
+ backtrace_for_thread(threadid, text);
+ text.format("\n");
+ }
+
+ *trace = text.release();
+ return 0;
+}
+
+#else /* TS_USE_REMOTE_UNWINDING */
+
+int
+ServerBacktrace(unsigned /* options */, int pid, char **trace)
+{
+ *trace = nullptr;
+ return -1;
+}
+
+#endif
diff --git a/src/traffic_crashlog/traffic_crashlog.cc
b/src/traffic_crashlog/traffic_crashlog.cc
index 3b922a0386..9b90d4f8b7 100644
--- a/src/traffic_crashlog/traffic_crashlog.cc
+++ b/src/traffic_crashlog/traffic_crashlog.cc
@@ -88,46 +88,27 @@ crashlog_open(const char *path)
return (fd == -1) ? nullptr : fdopen(fd, "w");
}
-static unsigned
-max_passwd_size()
-{
-#if defined(_SC_GETPW_R_SIZE_MAX)
- long val = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (val > 0) {
- return static_cast<unsigned>(val);
- }
-#endif
-
- return 4096;
-}
+extern int ServerBacktrace(unsigned /* options */, int pid, char **trace);
-static void
-change_privileges()
+bool
+crashlog_write_backtrace(FILE *fp, pid_t pid, const crashlog_target &)
{
- struct passwd *pwd;
- struct passwd pbuf;
- char buf[max_passwd_size()];
+ char *trace = nullptr;
+ int mgmterr;
- if (getpwnam_r(user, &pbuf, buf, sizeof(buf), &pwd) != 0) {
- Error("missing password database entry for username '%s': %s", user,
strerror(errno));
- return;
- }
-
- if (pwd == nullptr) {
- // Password entry not found ...
- Error("missing password database entry for '%s'", user);
- return;
- }
+ // NOTE: sometimes we can't get a backtrace because the ptrace attach will
fail with
+ // EPERM. I've seen this happen when a debugger is attached, which makes
sense, but it
+ // can also happen without a debugger. Possibly in that case, there is a
race with the
+ // kernel locking the process information?
- if (setegid(pwd->pw_gid) != 0) {
- Error("setegid(%d) failed: %s", pwd->pw_gid, strerror(errno));
- return;
+ if ((mgmterr = ServerBacktrace(0, static_cast<int>(pid), &trace)) != 0) {
+ fprintf(fp, "Unable to retrieve backtrace: %d\n", mgmterr);
+ return false;
}
- if (setreuid(pwd->pw_uid, 0) != 0) {
- Error("setreuid(%d, %d) failed: %s", pwd->pw_uid, 0, strerror(errno));
- return;
- }
+ fprintf(fp, "%s", trace);
+ free(trace);
+ return true;
}
int
@@ -145,13 +126,6 @@ main(int /* argc ATS_UNUSED */, const char **argv)
// Process command line arguments and dump into variables
process_args(&version, argument_descriptions,
countof(argument_descriptions), argv);
- // XXX This is a hack. traffic_manager starts traffic_server with the euid
of the admin user. We are still
- // privileged, but won't be able to open files in /proc or ptrace the
target. This really should be fixed
- // in traffic_manager.
- if (getuid() == 0) {
- change_privileges();
- }
-
if (wait_mode) {
EnableDeathSignal(SIGKILL);
kill(getpid(), SIGSTOP);
@@ -240,8 +214,10 @@ main(int /* argc ATS_UNUSED */, const char **argv)
crashlog_write_registers(fp, target);
fprintf(fp, "\n");
- crashlog_write_procstatus(fp, target);
+ crashlog_write_backtrace(fp, parent, target);
+ fprintf(fp, "\n");
+ crashlog_write_procstatus(fp, target);
fprintf(fp, "\n");
crashlog_write_proclimits(fp, target);