TS-3145: implement remote backtrace for traffic_server

Implement the ServerBacktrace API in traffic_manager using libunwind
and ptrace(2). This allow operators to log a back trace of all the
traffic_server threads.

Add the --backtrace option to traffic_line to display the backtrace.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/3e5a3f13
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/3e5a3f13
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/3e5a3f13

Branch: refs/heads/master
Commit: 3e5a3f134ca08a364e9ec38f02fb7cd85ba5425d
Parents: d27b31b
Author: James Peach <[email protected]>
Authored: Tue Sep 30 15:22:20 2014 -0700
Committer: James Peach <[email protected]>
Committed: Sat Oct 18 12:24:23 2014 -0700

----------------------------------------------------------------------
 cmd/traffic_line/traffic_line.cc |  13 +++
 cmd/traffic_manager/Makefile.am  |   1 +
 configure.ac                     |  12 +++
 lib/ts/ink_cap.cc                |   4 +
 lib/ts/ink_config.h.in           |   1 +
 lib/ts/ink_file.h                |  12 +++
 mgmt/api/CoreAPI.cc              | 187 ++++++++++++++++++++++++++++++++--
 mgmt/api/CoreAPI.h               |   1 +
 mgmt/api/CoreAPIRemote.cc        |  42 ++++++++
 mgmt/api/INKMgmtAPI.cc           |  17 ++++
 mgmt/api/Makefile.am             |   3 +-
 mgmt/api/NetworkMessage.cc       |   2 +
 mgmt/api/NetworkMessage.h        |   1 +
 mgmt/api/TSControlMain.cc        |  22 +++-
 mgmt/api/include/mgmtapi.h       |   9 ++
 15 files changed, 314 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/cmd/traffic_line/traffic_line.cc
----------------------------------------------------------------------
diff --git a/cmd/traffic_line/traffic_line.cc b/cmd/traffic_line/traffic_line.cc
index d5356c2..e40ecf5 100644
--- a/cmd/traffic_line/traffic_line.cc
+++ b/cmd/traffic_line/traffic_line.cc
@@ -52,6 +52,7 @@ static char ZeroNode[1024];
 static char StorageCmdOffline[1024];
 static int ShowAlarms;
 static int ShowStatus;
+static int ShowBacktrace;
 static char ClearAlarms[1024];
 static int VersionFlag;
 static char viaHeader[1024];
@@ -389,6 +390,17 @@ handleArgInvocation()
       break;
     }
     return TS_ERR_OKAY;
+  } else if (ShowBacktrace == 1) {
+    TSString trace = NULL;
+    TSMgmtError err;
+
+    err = TSProxyBacktraceGet(0, &trace);
+    if (err == TS_ERR_OKAY) {
+      printf("%s\n", trace);
+      TSfree(trace);
+    }
+
+    return err;
   } else if (*ReadVar != '\0') {        // Handle a value read
     if (*SetVar != '\0' || *VarValue != '\0') {
       fprintf(stderr, "%s: Invalid Argument Combination: Can not read and set 
values at the same time\n", programName);
@@ -566,6 +578,7 @@ main(int /* argc ATS_UNUSED */, char **argv)
     {"alarms", '-', "Show all alarms", "F", &ShowAlarms, NULL, NULL},
     {"clear_alarms", '-', "Clear specified, or all,  alarms", "S1024", 
&ClearAlarms, NULL, NULL},
     {"status", '-', "Show proxy server status", "F", &ShowStatus, NULL, NULL},
+    {"backtrace", '-', "Show proxy stack backtrace", "F", &ShowBacktrace, 
NULL, NULL},
     {"version", 'V', "Print Version Id", "T", &VersionFlag, NULL, NULL},
     {"decode_via", '-', "Decode Via Header", "S1024", &viaHeader, NULL, NULL},
   };

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/cmd/traffic_manager/Makefile.am
----------------------------------------------------------------------
diff --git a/cmd/traffic_manager/Makefile.am b/cmd/traffic_manager/Makefile.am
index 009671b..289c01c 100644
--- a/cmd/traffic_manager/Makefile.am
+++ b/cmd/traffic_manager/Makefile.am
@@ -53,6 +53,7 @@ traffic_manager_LDADD = \
   $(top_builddir)/iocore/eventsystem/libinkevent.a \
   $(top_builddir)/proxy/shared/liberror.a \
   $(top_builddir)/proxy/shared/libdiagsconfig.a \
+  $(LIBUNWIND_LIBS) \
   @LIBRESOLV@ @LIBEXPAT@ @LIBPCRE@ @LIBTCL@ @LIBCAP@ @HWLOC_LIBS@ \
   -lm
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index ed54039..c5f31b7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1200,6 +1200,18 @@ fi
 AC_SUBST(execinfoh)
 AC_SUBST(has_backtrace)
 
+# Remote process unwinding is only implemented on Linux because it depends on 
various Linux-specific
+# features such as /proc filesystem nodes, ptrace(2) and waitpid(2) extensions.
+AS_IF([test "$host_os_def" = "linux"], [
+  PKG_CHECK_MODULES([LIBUNWIND], [libunwind-ptrace], [
+    enable_remote_unwinding=yes
+  ], [
+    dnl no remote unwind support
+  ])
+])
+TS_ARG_ENABLE_VAR([use], [remote_unwinding])
+AC_SUBST(use_remote_unwinding)
+
 use_epoll=0
 use_kqueue=0
 use_port=0

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/lib/ts/ink_cap.cc
----------------------------------------------------------------------
diff --git a/lib/ts/ink_cap.cc b/lib/ts/ink_cap.cc
index e49ec6f..66af2e2 100644
--- a/lib/ts/ink_cap.cc
+++ b/lib/ts/ink_cap.cc
@@ -360,6 +360,10 @@ elevateFileAccess(unsigned level, bool state)
 ElevateAccess::ElevateAccess(const bool state, unsigned lvl)
   : elevated(false), saved_uid(geteuid()), level(lvl)
 {
+  // XXX Squash a clang [-Wunused-private-field] warning. The right solution 
is probably to extract
+  // the capabilities into a policy class.
+  (void)level;
+
   if (state == true) {
     elevate();
 #if !TS_USE_POSIX_CAP

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/lib/ts/ink_config.h.in
----------------------------------------------------------------------
diff --git a/lib/ts/ink_config.h.in b/lib/ts/ink_config.h.in
index cde3103..c9ca78f 100644
--- a/lib/ts/ink_config.h.in
+++ b/lib/ts/ink_config.h.in
@@ -74,6 +74,7 @@
 #define TS_USE_LINUX_NATIVE_AIO        @use_linux_native_aio@
 #define TS_USE_INTERIM_CACHE           @has_interim_cache@
 
+#define TS_USE_REMOTE_UNWINDING               @use_remote_unwinding@
 
 /* OS API definitions */
 #define GETHOSTBYNAME_R_GLIBC2         @gethostbyname_r_glibc2@

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/lib/ts/ink_file.h
----------------------------------------------------------------------
diff --git a/lib/ts/ink_file.h b/lib/ts/ink_file.h
index 326710c..1feda36 100644
--- a/lib/ts/ink_file.h
+++ b/lib/ts/ink_file.h
@@ -118,4 +118,16 @@ struct ink_device_geometry
 
 bool ink_file_get_geometry(int fd, ink_device_geometry& geometry);
 
+// Is the given path "."?
+static inline bool
+isdot(const char * path) {
+  return path[0] == '.' && path[1] == '\0';
+}
+
+// Is the given path ".."?
+static inline bool
+isdotdot(const char * path) {
+  return path[0] == '.' && path[1] == '.' && path[2] == '\0';
+}
+
 #endif // _ink_file_h_

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/CoreAPI.cc
----------------------------------------------------------------------
diff --git a/mgmt/api/CoreAPI.cc b/mgmt/api/CoreAPI.cc
index 258ffe3..0c13b65 100644
--- a/mgmt/api/CoreAPI.cc
+++ b/mgmt/api/CoreAPI.cc
@@ -46,6 +46,7 @@
 #include "CfgContextUtils.h"
 #include "EventCallback.h"
 #include "I_Layout.h"
+#include "ink_cap.h"
 
 // global variable
 CallbackTable *local_event_callbacks;
@@ -87,7 +88,6 @@ Terminate()
   return TS_ERR_OKAY;
 }
 
-
 /*-------------------------------------------------------------------------
  * Diags
  *-------------------------------------------------------------------------
@@ -137,7 +137,6 @@ Diags(TSDiagsT mode, const char *fmt, va_list ap)
   }
 }
 
-
 /***************************************************************************
  * Control Operations
  ***************************************************************************/
@@ -228,6 +227,181 @@ Lerror:
   return TS_ERR_FAIL;          /* failed to set proxy  state */
 }
 
+#if TS_USE_REMOTE_UNWINDING
+
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+#include <sys/ptrace.h>
+#include <cxxabi.h>
+
+typedef Vec<pid_t> threadlist;
+
+static threadlist
+threads_for_process(pid_t proc)
+{
+  DIR * dir = NULL;
+  struct dirent * entry = NULL;
+
+  char path[64];
+  threadlist threads;
+
+  if (snprintf(path, sizeof(path), "/proc/%ld/task", (long)proc) >= 
(int)sizeof(path)) {
+    goto done;
+  }
+
+  dir = opendir(path);
+  if (dir == NULL) {
+    goto done;
+  }
+
+  while ((entry = readdir(dir))) {
+    pid_t threadid;
+
+    if (isdot(entry->d_name) || isdotdot(entry->d_name)) {
+      continue;
+    }
+
+    threadid = strtol(entry->d_name, NULL, 10);
+    if (threadid > 0) {
+      threads.push_back(threadid);
+      Debug("backtrace", "found thread %ld", (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 = NULL;
+  unw_cursor_t cursor;
+  void * ap = NULL;
+  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)", (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",  
(long)threadid, (long)target,
+      WIFSTOPPED(status) ? "STOPPED" : "???");
+  if (target < 0) {
+    goto done;
+  }
+
+  ap = _UPT_create(threadid);
+  Debug("backtrace", "created UPT %p", ap);
+  if (ap == NULL) {
+    goto done;
+  }
+
+  addr_space = unw_create_addr_space(&_UPT_accessors, 0 /* byteorder */);
+  Debug("backtrace", "created address space %p", addr_space);
+  if (addr_space == NULL) {
+    goto done;
+  }
+
+  status = unw_init_remote(&cursor, addr_space, ap);
+  Debug("backtrace", "unw_init_remote(...) -> %d", 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, NULL, NULL, &status);
+      text.format("%-4u 0x%016llx %s + %p\n", level, (unsigned long long)ip, 
name ? name : buf, (void *)offset);
+      free(name);
+    } else {
+      text.format("%-4u 0x%016llx 0x0 + %p\n", level, (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)", (long)target, 
status, errno);
+}
+
+TSMgmtError
+ServerBacktrace(unsigned /* options */, char ** trace)
+{
+  *trace = NULL;
+
+  // Unfortunately, we need to be privileged here. We either need to be root 
or to be holding
+  // the CAP_SYS_PTRACE capability. Even though we are the parent 
traffic_manager, it is not
+  // traceable without privilege because the process credentials do not match.
+  ElevateAccess access(true, ElevateAccess::TRACE_PRIVILEGE);
+  threadlist    threads(threads_for_process(lmgmt->watched_process_pid));
+  textBuffer    text(0);
+
+  Debug("backtrace", "tracing %zd threads for traffic_server PID %ld", 
threads.count(), (long)lmgmt->watched_process_pid);
+  for_Vec(pid_t, threadid, threads) {
+    Debug("backtrace", "tracing thread %zd", (long)threadid);
+    // Get the thread name using /proc/PID/comm
+    ats_scoped_fd fd;
+    char threadname[128];
+
+    snprintf(threadname, sizeof(threadname), "/proc/%ld/comm", (long)threadid);
+    fd = open(threadname, O_RDONLY);
+    if (fd) {
+      text.format("Thread %ld, ", (long)threadid);
+      text.readFromFD(fd);
+      text.chomp();
+    } else {
+      text.format("Thread %ld", (long)threadid);
+    }
+
+    text.format(":\n");
+
+    backtrace_for_thread(threadid, text);
+    text.format("\n");
+  }
+
+  *trace = text.release();
+  return TS_ERR_OKAY;
+}
+
+#else /* TS_USE_REMOTE_UNWINDING */
+
+TSMgmtError
+ServerBacktrace(unsigned /* options */, char ** trace)
+{
+  *trace = NULL;
+  return TS_ERR_NOT_SUPPORTED;
+}
+
+#endif
+
 /*-------------------------------------------------------------------------
  * Reconfigure
  *-------------------------------------------------------------------------
@@ -350,7 +524,6 @@ MgmtRecordGet(const char *rec_name, TSRecordEle * rec_ele)
     if (!varStrFromName(rec_name, rec_val, MAX_BUF_SIZE))
       return TS_ERR_FAIL;
 
-
     if (rec_val[0] != '\0') {   // non-NULL string value
       // allocate memory & duplicate string value
       str_val = ats_strdup(rec_val);
@@ -414,7 +587,6 @@ determine_action_need(const char *rec_name)
   return TS_ACTION_UNDEFINED;  // ERROR
 }
 
-
 /*-------------------------------------------------------------------------
  * MgmtRecordSet
  *-------------------------------------------------------------------------
@@ -445,7 +617,6 @@ MgmtRecordSet(const char *rec_name, const char *val, 
TSActionNeedT * action_need
   return TS_ERR_FAIL;
 }
 
-
 /*-------------------------------------------------------------------------
  * MgmtRecordSetInt
  *-------------------------------------------------------------------------
@@ -468,7 +639,6 @@ MgmtRecordSetInt(const char *rec_name, MgmtInt int_val, 
TSActionNeedT * action_n
   return MgmtRecordSet(rec_name, str_val, action_need);
 }
 
-
 /*-------------------------------------------------------------------------
  * MgmtRecordSetCounter
  *-------------------------------------------------------------------------
@@ -489,7 +659,6 @@ MgmtRecordSetCounter(const char *rec_name, MgmtIntCounter 
counter_val, TSActionN
   return MgmtRecordSet(rec_name, str_val, action_need);
 }
 
-
 /*-------------------------------------------------------------------------
  * MgmtRecordSetFloat
  *-------------------------------------------------------------------------
@@ -511,7 +680,6 @@ MgmtRecordSetFloat(const char *rec_name, MgmtFloat 
float_val, TSActionNeedT * ac
   return MgmtRecordSet(rec_name, str_val, action_need);
 }
 
-
 /*-------------------------------------------------------------------------
  * MgmtRecordSetString
  *-------------------------------------------------------------------------
@@ -523,7 +691,6 @@ MgmtRecordSetString(const char *rec_name, const char 
*string_val, TSActionNeedT
   return MgmtRecordSet(rec_name, string_val, action_need);
 }
 
-
 /**************************************************************************
  * FILE OPERATIONS
  *************************************************************************/
@@ -550,7 +717,6 @@ ReadFile(TSFileNameT file, char **text, int *size, int 
*version)
 
   Debug("FileOp", "[get_lines_from_file] START\n");
 
-
   fname = filename_to_string(file);
   if (!fname)
     return TS_ERR_READ_FILE;
@@ -597,7 +763,6 @@ WriteFile(TSFileNameT file, const char *text, int size, int 
version)
   int ret;
   version_t ver;
 
-
   fname = filename_to_string(file);
   if (!fname)
     return TS_ERR_WRITE_FILE;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/CoreAPI.h
----------------------------------------------------------------------
diff --git a/mgmt/api/CoreAPI.h b/mgmt/api/CoreAPI.h
index 2585b16..a1b2f89 100644
--- a/mgmt/api/CoreAPI.h
+++ b/mgmt/api/CoreAPI.h
@@ -45,6 +45,7 @@ void Diags(TSDiagsT mode, const char * fmt, va_list ap);
  ***************************************************************************/
 TSProxyStateT ProxyStateGet();
 TSMgmtError ProxyStateSet(TSProxyStateT state, TSCacheClearT clear);
+TSMgmtError ServerBacktrace(unsigned options, char ** trace);
 
 TSMgmtError Reconfigure();         // TS reread config files
 TSMgmtError Restart(bool cluster); //restart TM

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/CoreAPIRemote.cc
----------------------------------------------------------------------
diff --git a/mgmt/api/CoreAPIRemote.cc b/mgmt/api/CoreAPIRemote.cc
index 5ebfa36..00b2bda 100644
--- a/mgmt/api/CoreAPIRemote.cc
+++ b/mgmt/api/CoreAPIRemote.cc
@@ -348,6 +348,47 @@ ProxyStateSet(TSProxyStateT state, TSCacheClearT clear)
 }
 
 TSMgmtError
+ServerBacktrace(unsigned options, char ** trace)
+{
+  ink_release_assert(trace != NULL);
+  TSMgmtError ret;
+  MgmtMarshallInt optype = SERVER_BACKTRACE;
+  MgmtMarshallInt err;
+  MgmtMarshallInt flags = options;
+  MgmtMarshallData reply = { NULL, 0 };
+  MgmtMarshallString strval = NULL;
+
+  ret = MGMTAPI_SEND_MESSAGE(main_socket_fd, SERVER_BACKTRACE, &optype, 
&flags);
+  if (ret != TS_ERR_OKAY) {
+    goto fail;
+  }
+
+  ret = recv_mgmt_message(main_socket_fd, reply);
+  if (ret != TS_ERR_OKAY) {
+    goto fail;
+  }
+
+  ret = recv_mgmt_response(reply.ptr, reply.len, SERVER_BACKTRACE, &err, 
&strval);
+  if (ret != TS_ERR_OKAY) {
+    goto fail;
+  }
+
+  if (err != TS_ERR_OKAY) {
+    ret = (TSMgmtError)err;
+    goto fail;
+  }
+
+  ats_free(reply.ptr);
+  *trace = strval;
+  return TS_ERR_OKAY;
+
+fail:
+  ats_free(reply.ptr);
+  ats_free(strval);
+  return ret;
+}
+
+TSMgmtError
 Reconfigure()
 {
   TSMgmtError ret;
@@ -942,3 +983,4 @@ StatsReset(bool cluster, const char * stat_name)
   ret = MGMTAPI_SEND_MESSAGE(main_socket_fd, op, &optype, &name);
   return (ret == TS_ERR_OKAY) ? parse_generic_response(op, main_socket_fd) : 
ret;
 }
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/INKMgmtAPI.cc
----------------------------------------------------------------------
diff --git a/mgmt/api/INKMgmtAPI.cc b/mgmt/api/INKMgmtAPI.cc
index 32879b4..814a90f 100644
--- a/mgmt/api/INKMgmtAPI.cc
+++ b/mgmt/api/INKMgmtAPI.cc
@@ -1756,6 +1756,20 @@ TSProxyStateSet(TSProxyStateT proxy_state, TSCacheClearT 
clear)
   return ProxyStateSet(proxy_state, clear);
 }
 
+tsapi TSMgmtError
+TSProxyBacktraceGet(unsigned options, TSString * trace)
+{
+  if (options != 0) {
+    return TS_ERR_PARAMS;
+  }
+
+  if (trace == NULL) {
+    return TS_ERR_PARAMS;
+  }
+
+  return ServerBacktrace(options, trace);
+}
+
 /* TSReconfigure: tell traffic_server to re-read its configuration files
  * Input:  <none>
  * Output: TSMgmtError
@@ -1881,6 +1895,9 @@ TSGetErrorMessage(TSMgmtError err_id)
   case TS_ERR_FAIL:
     snprintf(msg, sizeof(msg), "[%d] Generic Fail message (ie. CoreAPI 
call).", err_id);
     break;
+  case TS_ERR_NOT_SUPPORTED:
+    snprintf(msg, sizeof(msg), "[%d] Operation not supported on this 
platform.", err_id);
+    break;
 
   default:
     snprintf(msg, sizeof(msg), "[%d] Invalid error type.", err_id);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/Makefile.am
----------------------------------------------------------------------
diff --git a/mgmt/api/Makefile.am b/mgmt/api/Makefile.am
index 999d93d..0845ea4 100644
--- a/mgmt/api/Makefile.am
+++ b/mgmt/api/Makefile.am
@@ -28,7 +28,8 @@ AM_CPPFLAGS = \
   -I$(top_srcdir)/mgmt/utils \
   -I$(top_srcdir)/mgmt/api/include \
   -I$(top_srcdir)/lib \
-  -I$(top_builddir)/lib
+  -I$(top_builddir)/lib \
+  $(LIBUNWIND_CFLAGS)
 
 noinst_LTLIBRARIES = libmgmtapilocal.la libmgmtapi.la
 lib_LTLIBRARIES = libtsmgmt.la

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/NetworkMessage.cc
----------------------------------------------------------------------
diff --git a/mgmt/api/NetworkMessage.cc b/mgmt/api/NetworkMessage.cc
index 1869bf9..c0c0b4c 100644
--- a/mgmt/api/NetworkMessage.cc
+++ b/mgmt/api/NetworkMessage.cc
@@ -64,6 +64,7 @@ static const struct NetCmdOperation requests[] = {
   /* STORAGE_DEVICE_CMD_OFFLINE */ { 2, { MGMT_MARSHALL_INT, 
MGMT_MARSHALL_STRING } },
   /* RECORD_MATCH_GET           */ { 2, { MGMT_MARSHALL_INT, 
MGMT_MARSHALL_STRING } },
   /* API_PING                   */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT 
} },
+  /* SERVER_BACKTRACE           */ { 2, { MGMT_MARSHALL_INT, MGMT_MARSHALL_INT 
} },
 };
 
 // Responses always begin with a TSMgmtError code, followed by additional 
fields.
@@ -93,6 +94,7 @@ static const struct NetCmdOperation responses[] = {
   /* STORAGE_DEVICE_CMD_OFFLINE */ { 1, { MGMT_MARSHALL_INT } },
   /* RECORD_MATCH_GET           */ { 4, { MGMT_MARSHALL_INT, 
MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA } },
   /* API_PING                   */ { 0, {} }, // no reply
+  /* SERVER_BACKTRACE           */ { 2, { MGMT_MARSHALL_INT, 
MGMT_MARSHALL_STRING } },
 };
 
 #define GETCMD(ops, optype, cmd) do { \

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/NetworkMessage.h
----------------------------------------------------------------------
diff --git a/mgmt/api/NetworkMessage.h b/mgmt/api/NetworkMessage.h
index ae94185..e8dd58c 100644
--- a/mgmt/api/NetworkMessage.h
+++ b/mgmt/api/NetworkMessage.h
@@ -59,6 +59,7 @@ typedef enum
   STORAGE_DEVICE_CMD_OFFLINE,
   RECORD_MATCH_GET,
   API_PING,
+  SERVER_BACKTRACE,
   UNDEFINED_OP /* This must be last */
 } OpType;
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/TSControlMain.cc
----------------------------------------------------------------------
diff --git a/mgmt/api/TSControlMain.cc b/mgmt/api/TSControlMain.cc
index ba5ba3f..474918f 100644
--- a/mgmt/api/TSControlMain.cc
+++ b/mgmt/api/TSControlMain.cc
@@ -967,6 +967,25 @@ handle_api_ping(int /* fd */, void * req, size_t reqlen)
   return recv_mgmt_request(req, reqlen, API_PING, &optype, &stamp);
 }
 
+static TSMgmtError
+handle_server_backtrace(int fd, void * req, size_t reqlen)
+{
+  MgmtMarshallInt optype;
+  MgmtMarshallInt options;
+  MgmtMarshallString trace = NULL;
+  MgmtMarshallInt err;
+
+  err = recv_mgmt_request(req, reqlen, SERVER_BACKTRACE, &optype, &options);
+  if (err == TS_ERR_OKAY) {
+    err = ServerBacktrace(options, &trace);
+  }
+
+  err = send_mgmt_response(fd, SERVER_BACKTRACE, &err, &trace);
+  ats_free(trace);
+
+  return (TSMgmtError)err;
+}
+
 typedef TSMgmtError (*control_message_handler)(int, void *, size_t);
 
 static const control_message_handler handlers[] = {
@@ -994,7 +1013,8 @@ static const control_message_handler handlers[] = {
   handle_stats_reset,                 // STATS_RESET_CLUSTER
   handle_storage_device_cmd_offline,  // STORAGE_DEVICE_CMD_OFFLINE
   handle_record_match,                // RECORD_MATCH_GET
-  handle_api_ping                     // API_PING
+  handle_api_ping,                    // API_PING
+  handle_server_backtrace             // SERVER_BACKTRACE
 };
 
 static TSMgmtError

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/3e5a3f13/mgmt/api/include/mgmtapi.h
----------------------------------------------------------------------
diff --git a/mgmt/api/include/mgmtapi.h b/mgmt/api/include/mgmtapi.h
index c9fc90d..581c69e 100644
--- a/mgmt/api/include/mgmtapi.h
+++ b/mgmt/api/include/mgmtapi.h
@@ -71,6 +71,8 @@ extern "C"
     TS_ERR_SYS_CALL,           /* Error in basic system call, eg. malloc */
     TS_ERR_PARAMS,             /* Invalid parameters for a fn */
 
+    TS_ERR_NOT_SUPPORTED,      /* Operation not supported */
+
     TS_ERR_FAIL
   } TSMgmtError;
 
@@ -961,6 +963,13 @@ extern "C"
  */
   tsapi TSMgmtError TSProxyStateSet(TSProxyStateT proxy_state, TSCacheClearT 
clear);
 
+/* TSProxyBacktraceGet: get a backtrace of the proxy
+ * Input:  unsigned options - stack trace options
+ * Output: formatted backtrace of the proxy
+ *     the caller must free this with TSfree
+ */
+  tsapi TSMgmtError TSProxyBacktraceGet(unsigned, TSString *);
+
 /* TSReconfigure: tell traffic_server to re-read its configuration files
  * Input:  <none>
  * Output: TSMgmtError

Reply via email to