diff --git a/Action.c b/Action.c index 56fb3d0cb..9639a931b 100644 --- a/Action.c +++ b/Action.c @@ -27,7 +27,9 @@ in the source distribution for its full text. #include "ListItem.h" #include "Macros.h" #include "MainPanel.h" +#include "Object.h" #include "OpenFilesScreen.h" +#include "Panel.h" #include "Process.h" #include "ProcessLocksScreen.h" #include "ProvideCurses.h" @@ -47,6 +49,10 @@ in the source distribution for its full text. #include "AffinityPanel.h" #endif +#if defined(HAVE_BACKTRACE) +#include "BacktraceScreen.h" +#endif + Object* Action_pickFromVector(State* st, Panel* list, int x, bool follow) { MainPanel* mainPanel = st->mainPanel; @@ -595,6 +601,24 @@ static Htop_Reaction actionShowLocks(State* st) { return HTOP_REFRESH | HTOP_REDRAW_BAR; } +#if defined(HAVE_BACKTRACE) +static Htop_Reaction actionBacktrace(State *st) { + const Process* process = (Process*) Panel_getSelected((Panel*)st->mainPanel); + if (!process) + return HTOP_OK; + + BacktracePanel *panel = BacktracePanel_new(process, st->host->settings); + ScreenManager *screenManager = ScreenManager_new(NULL, st->host, st, false); + ScreenManager_add(screenManager, (Panel *)panel, 0); + + ScreenManager_run(screenManager, NULL, NULL, NULL); + BacktracePanel_delete((Object *)panel); + ScreenManager_delete(screenManager); + + return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; +} +#endif + static Htop_Reaction actionStrace(State* st) { if (!Action_writeableProcess(st)) return HTOP_OK; @@ -678,6 +702,9 @@ static const struct { { .key = " F8 [: ", .roInactive = true, .info = "lower priority (+ nice)" }, #if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)) { .key = " a: ", .roInactive = true, .info = "set CPU affinity" }, +#endif +#if defined(HAVE_BACKTRACE) + { .key = " b: ", .roInactive = false, .info = "show process backtrace" }, #endif { .key = " e: ", .roInactive = false, .info = "show process environment" }, { .key = " i: ", .roInactive = true, .info = "set IO priority" }, @@ -918,6 +945,9 @@ void Action_setBindings(Htop_Action* keys) { keys['\\'] = actionIncFilter; keys[']'] = actionHigherPriority; keys['a'] = actionSetAffinity; +#if defined(HAVE_BACKTRACE) + keys['b'] = actionBacktrace; +#endif keys['c'] = actionTagAllChildren; keys['e'] = actionShowEnvScreen; keys['h'] = actionHelp; diff --git a/BacktraceScreen.c b/BacktraceScreen.c new file mode 100644 index 000000000..0286ba36d --- /dev/null +++ b/BacktraceScreen.c @@ -0,0 +1,421 @@ +/* +htop - BacktraceScreen.h +(C) 2024 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "BacktraceScreen.h" + +#if defined(HAVE_BACKTRACE) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "FunctionBar.h" +#include "ListItem.h" +#include "Macros.h" +#include "Object.h" +#include "Panel.h" +#include "Platform.h" +#include "Process.h" +#include "ProvideCurses.h" +#include "RichString.h" +#include "Settings.h" +#include "Vector.h" +#include "XUtils.h" + + +#define BACKTRACE_FRAME_HEADER_INDEX -1 +#define MAX_HEX_ADDR_STR_LEN_32 (strlen("0x") + 8) +#define MAX_HEX_ADDR_STR_LEN_64 (strlen("0x") + 16) + +typedef enum BacktracePanelOptions_ { + OPTION_NAME_DEMANGLE, + OPTION_NAME_RAW, + OPTION_OBJECT_FULL_PATH, + OPTION_OBJECT_BASENAME, + LAST_PANEL_OPTION, +} BacktracePanelOptions; + +static const char* const BacktracePanel_options[LAST_PANEL_OPTION] = { + [OPTION_NAME_DEMANGLE] = "Demangle", + [OPTION_NAME_RAW] = "Raw", + [OPTION_OBJECT_FULL_PATH] = "Full Path", + [OPTION_OBJECT_BASENAME] = "Basename", +}; + +typedef enum BacktraceFrameHeaders_ { + HEADER_NUMBER_FRAME, + HEADER_ADDRESS, + HEADER_NAME, + HEADER_PATH, + LAST_PANEL_HEADER, +} BacktracePanelHeaders; + +static const char* const BacktraceFrame_headerFields[LAST_PANEL_HEADER] = { + [HEADER_NUMBER_FRAME] = "#", + [HEADER_ADDRESS] = "ADDRESS", + [HEADER_NAME] = "NAME", + [HEADER_PATH] = "PATH", +}; + +static const char* const BacktraceScreenFunctions[] = { + "Refresh", +#if defined(HAVE_DEMANGLING) + BacktracePanel_options[OPTION_NAME_RAW], +#endif + BacktracePanel_options[OPTION_OBJECT_FULL_PATH], + "Done ", + NULL +}; + +static const char* const BacktraceScreenKeys[] = { + "F1", +#if defined(HAVE_DEMANGLING) + "F2", +#endif + "F3", + "Esc", + NULL +}; + +static const int BacktraceScreenEvents[] = { + KEY_F(1), +#if defined(HAVE_DEMANGLING) + KEY_F(2), +#endif + KEY_F(3), + 27, +}; + +ATTR_NONNULL static void BacktraceFrame_displayHeader(const BacktraceFrame* frame, RichString* out) { + const BacktracePanelPrintingHelper* printingHelper = &frame->backtracePanel->printingHelper; + const BacktraceScreenDisplayOptions displayOptions = frame->backtracePanel->displayOptions; + + int maxFunctionNameLength = printingHelper->maxFuncNameLen; + if (!!(displayOptions & DEMANGLE_NAME_FUNCTION) && + printingHelper->maxDemangledFuncNameLen > 0) { + maxFunctionNameLength = printingHelper->maxDemangledFuncNameLen; + } + + /* + * The parameters for printf are of type int. + * A check is needed to prevent integer overflow. + */ + assert(printingHelper->maxFrameNumLen <= INT_MAX); + assert(printingHelper->maxAddrLen <= INT_MAX); + assert(printingHelper->maxDemangledFuncNameLen <= INT_MAX); + assert(printingHelper->maxObjPathLen <= INT_MAX); + + char* line = NULL; + int len = xAsprintf(&line, "%*s %-*s %-*s %-*s", + (int)printingHelper->maxFrameNumLen, BacktraceFrame_headerFields[HEADER_NUMBER_FRAME], + (int)printingHelper->maxAddrLen, BacktraceFrame_headerFields[HEADER_ADDRESS], + (int)maxFunctionNameLength, BacktraceFrame_headerFields[HEADER_NAME], + (int)printingHelper->maxObjPathLen, BacktraceFrame_headerFields[HEADER_PATH] + ); + + RichString_appendnAscii(out, CRT_colors[BACKTRACE_HEADER], line, len); + free(line); +} + +static void BacktraceFrame_highlightBasename(const BacktraceFrame* frame, RichString* out, char* line, int objectPathStart) { + assert(objectPathStart >= 0); + + char* basename = frame->backtracePanel->process->procExe ? + frame->backtracePanel->process->procExe + frame->backtracePanel->process->procExeBasenameOffset : NULL; + char* object = line ? line + objectPathStart : NULL; + if (!basename || !object) { + return; + } + + char* lastSlash = strrchr(object, '/'); + if (lastSlash) { + object = lastSlash + 1; + } + + char* objectTrimmed = String_trim(object); + if (String_eq(basename, objectTrimmed)) { + RichString_setAttrn(out, CRT_colors[PROCESS_BASENAME], object - line, strlen(objectTrimmed)); + } + + free(objectTrimmed); +} + +static void BacktraceFrame_display(const Object* super, RichString* out) { + const BacktraceFrame* frame = (const BacktraceFrame* )super; + assert(frame); + + if (frame->index == BACKTRACE_FRAME_HEADER_INDEX) { + BacktraceFrame_displayHeader(frame, out); + return; + } + + const BacktracePanelPrintingHelper* printingHelper = &frame->backtracePanel->printingHelper; + + const BacktraceScreenDisplayOptions displayOptions = frame->backtracePanel->displayOptions; + + char* functionName = frame->functionName; + int maxFunctionNameLength = printingHelper->maxFuncNameLen; + if (!!(displayOptions & DEMANGLE_NAME_FUNCTION) && + printingHelper->maxDemangledFuncNameLen > 0) { + maxFunctionNameLength = printingHelper->maxDemangledFuncNameLen; + if (frame->demangleFunctionName) { + functionName = frame->demangleFunctionName; + } + } + + char *completeFunctionName = NULL; + xAsprintf(&completeFunctionName, "%s+0x%zx", functionName, frame->offset); + + char* objectDisplayed = frame->objectName; + size_t objectLength = printingHelper->maxObjNameLen; + if (!!(displayOptions & SHOW_FULL_PATH_OBJECT)) { + objectDisplayed = frame->objectPath; + objectLength = printingHelper->maxObjPathLen; + } + + + size_t maxAddrLen = printingHelper->maxAddrLen - strlen("0x"); + char* line = NULL; + int objectPathStart = -1; + + /* + * The parameters for printf are of type int. + * A check is needed to prevent integer overflow. + */ + assert(printingHelper->maxFrameNumLen <= INT_MAX); + assert(maxAddrLen <= INT_MAX); + assert(maxFunctionNameLength <= INT_MAX); + assert(objectLength <= INT_MAX); + int len = xAsprintf(&line, "%*d 0x%0*zx %-*s %n%-*s", + (int)printingHelper->maxFrameNumLen, frame->index, + (int)maxAddrLen, frame->address, + (int)maxFunctionNameLength, completeFunctionName, + &objectPathStart, + (int)objectLength, objectDisplayed ? objectDisplayed : "-" + ); + + int colors = CRT_colors[DEFAULT_COLOR]; + if (!objectDisplayed) { + colors = CRT_colors[DYNAMIC_DARKGRAY]; + } + + RichString_appendnAscii(out, colors, line, len); + + BacktraceFrame_highlightBasename(frame, out, line, objectPathStart); + + free(completeFunctionName); + free(line); +} + +static int BacktraceFrame_compare(const void* object1, const void* object2) { + const BacktraceFrame* frame1 = (const BacktraceFrame*)object1; + const BacktraceFrame* frame2 = (const BacktraceFrame*)object2; + return String_eq(frame1->functionName, frame2->functionName); +} + +static void BacktraceFrame_delete(Object* object) { + BacktraceFrame* this = (BacktraceFrame*)object; + free(this->functionName); + free(this->demangleFunctionName); + free(this->objectPath); + free(this->objectName); + free(this); +} + +static void BacktracePanel_setError(BacktracePanel* this, const char* error) { + assert(error); + assert(!this->error); + + Panel* super = &this->super; + Panel_prune(super); + + Vector* lines = this->super.items; + if (lines) { + Vector_delete(lines); + } + + this->super.items = Vector_new(Class(ListItem), true, DEFAULT_SIZE); + Panel_set(super, 0, (Object*)ListItem_new(error, 0)); +} + +static void BacktracePanel_makePrintingHelper(const BacktracePanel* this, BacktracePanelPrintingHelper* printingHelper) { + Vector* lines = this->super.items; + size_t longestAddress = 0; + + for (int i = 0; i < Vector_size(lines); i++) { + BacktraceFrame* frame = (BacktraceFrame*)Vector_get(lines, i); + frame->backtracePanel = this; + + size_t digitOfOffsetFrame = strlen("+0x") + countDigit(frame->offset, 16); + if (frame->demangleFunctionName) { + size_t demangledFunctionNameLength = strlen(frame->demangleFunctionName) + digitOfOffsetFrame; + printingHelper->maxDemangledFuncNameLen = MAXIMUM(demangledFunctionNameLength, printingHelper->maxDemangledFuncNameLen); + } + + if (frame->functionName) { + size_t functionNameLength = strlen(frame->functionName) + digitOfOffsetFrame; + printingHelper->maxFuncNameLen = MAXIMUM(functionNameLength, printingHelper->maxFuncNameLen); + } + + if (frame->objectPath) { + size_t objectPathLength = strlen(frame->objectPath); + printingHelper->maxObjPathLen = MAXIMUM(objectPathLength, printingHelper->maxObjPathLen); + } + + if (frame->objectName) { + size_t objectNameLength = strlen(frame->objectName); + printingHelper->maxObjNameLen = MAXIMUM(objectNameLength, printingHelper->maxObjNameLen); + } + + longestAddress = MAXIMUM(frame->address, longestAddress); + } + + size_t addressLength = MAX_HEX_ADDR_STR_LEN_32; + if (longestAddress > UINT32_MAX) { + addressLength = MAX_HEX_ADDR_STR_LEN_64; + } + printingHelper->maxAddrLen = addressLength; + + size_t maxNumberFrameDigit = countDigit(Vector_size(lines), 10); + printingHelper->maxFrameNumLen = MAXIMUM(maxNumberFrameDigit, strlen(BacktraceFrame_headerFields[HEADER_NUMBER_FRAME])); +} + +static void BacktracePanel_populateFrames(BacktracePanel* this) { + if (this->error) { + return; + } + + char* error = NULL; + Vector* lines = this->super.items; + + BacktraceFrame* header = BacktraceFrame_new(); + header->index = BACKTRACE_FRAME_HEADER_INDEX; + Vector_add(lines, (Object*)header); + + Platform_getBacktrace(lines, this->process, &error); + if (error) { + BacktracePanel_setError(this, error); + free(error); + return; + } + + BacktracePanelPrintingHelper* printingHelper = &this->printingHelper; + BacktracePanel_makePrintingHelper(this, printingHelper); +} + +BacktracePanel* BacktracePanel_new(const Process* process, const Settings* settings) { + BacktracePanel* this = AllocThis(BacktracePanel); + this->process = process; + this->error = false; + this->printingHelper.maxObjPathLen = 0; + this->printingHelper.maxFuncNameLen = 0; + this->printingHelper.maxDemangledFuncNameLen = 0; + this->displayOptions = DEMANGLE_NAME_FUNCTION; + this->settings = settings; + + Panel* super = (Panel*) this; + Panel_init(super, 1, 1, 1, 1, Class(BacktraceFrame), true, + FunctionBar_new(BacktraceScreenFunctions, BacktraceScreenKeys, BacktraceScreenEvents) + ); + + char* header = NULL; + xAsprintf(&header, "Backtrace of '%s' (%d)", process->procComm, Process_getPid(process)); + Panel_setHeader(super, header); + free(header); + + BacktracePanel_populateFrames(this); + + if (settings->showProgramPath) { + this->displayOptions |= SHOW_FULL_PATH_OBJECT; + FunctionBar_setLabel(super->defaultBar, KEY_F(3), BacktracePanel_options[OPTION_OBJECT_BASENAME]); + } else { + this->displayOptions &= ~SHOW_FULL_PATH_OBJECT; + FunctionBar_setLabel(super->defaultBar, KEY_F(3), BacktracePanel_options[OPTION_OBJECT_FULL_PATH]); + } + + return this; +} + +BacktraceFrame* BacktraceFrame_new(void) { + BacktraceFrame* this = AllocThis(BacktraceFrame); + this->index = 0; + this->address = 0; + this->offset = 0; + this->functionName = NULL; + this->demangleFunctionName = NULL; + this->objectPath = NULL; + this->objectName = NULL; + this->isSignalFrame = false; + return this; +} + +static HandlerResult BacktracePanel_eventHandler(Panel* super, int ch) { + BacktracePanel* this = (BacktracePanel*)super; + BacktraceScreenDisplayOptions* displayOptions = &this->displayOptions; + + HandlerResult result = IGNORED; + switch (ch) { + case KEY_F(1): + Panel_prune(super); + BacktracePanel_populateFrames(this); + break; + +#if defined(HAVE_DEMANGLING) + case KEY_F(2): + if (!!(*displayOptions & DEMANGLE_NAME_FUNCTION)) { + *displayOptions &= ~DEMANGLE_NAME_FUNCTION; + FunctionBar_setLabel(super->defaultBar, KEY_F(2), BacktracePanel_options[OPTION_NAME_DEMANGLE]); + } else { + *displayOptions |= DEMANGLE_NAME_FUNCTION; + FunctionBar_setLabel(super->defaultBar, KEY_F(2), BacktracePanel_options[OPTION_NAME_RAW]); + } + this->super.needsRedraw = true; + break; +#endif + + case 'p': + case KEY_F(3): + if (!!(*displayOptions & SHOW_FULL_PATH_OBJECT)) { + *displayOptions &= ~SHOW_FULL_PATH_OBJECT; + FunctionBar_setLabel(super->defaultBar, KEY_F(3), BacktracePanel_options[OPTION_OBJECT_FULL_PATH]); + } else { + FunctionBar_setLabel(super->defaultBar, KEY_F(3), BacktracePanel_options[OPTION_OBJECT_BASENAME]); + *displayOptions |= SHOW_FULL_PATH_OBJECT; + } + this->super.needsRedraw = true; + break; + } + return result; +} + +void BacktracePanel_delete(Object* object) { + Panel_delete(object); +} + +const PanelClass BacktracePanel_class = { + .super = { + .extends = Class(Panel), + .delete = BacktracePanel_delete, + }, + .eventHandler = BacktracePanel_eventHandler, +}; + +const ObjectClass BacktraceFrame_class = { + .extends = Class(Object), + .compare = BacktraceFrame_compare, + .delete = BacktraceFrame_delete, + .display = BacktraceFrame_display, +}; +#endif diff --git a/BacktraceScreen.h b/BacktraceScreen.h new file mode 100644 index 000000000..9c7042c08 --- /dev/null +++ b/BacktraceScreen.h @@ -0,0 +1,66 @@ +#ifndef HEADER_BacktraceScreen +#define HEADER_BacktraceScreen +/* +htop - BacktraceScreen.h +(C) 2024 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include +#include + +#include "Object.h" +#include "Panel.h" +#include "Process.h" +#include "Settings.h" + + +typedef struct BacktracePanelPrintingHelper_ { + size_t maxAddrLen; + size_t maxDemangledFuncNameLen; + size_t maxFuncNameLen; + size_t maxFrameNumLen; + size_t maxObjPathLen; + size_t maxObjNameLen; +} BacktracePanelPrintingHelper; + +typedef enum BacktraceScreenDisplayOptions_ { + NO_OPTION = 0, + DEMANGLE_NAME_FUNCTION = 1 << 0, + SHOW_FULL_PATH_OBJECT = 1 << 1, +} BacktraceScreenDisplayOptions; + +typedef struct BacktracePanel_ { + Panel super; + const Process* process; + bool error; + BacktracePanelPrintingHelper printingHelper; + BacktraceScreenDisplayOptions displayOptions; + const Settings* settings; +} BacktracePanel; + +typedef struct BacktraceFrame_ { + Object super; + + int index; + size_t address; + size_t offset; + char* functionName; + char* demangleFunctionName; + bool isSignalFrame; + + char* objectPath; + char* objectName; + + const BacktracePanel* backtracePanel; +} BacktraceFrame; + +BacktracePanel* BacktracePanel_new(const Process* process, const Settings* settings); +void BacktracePanel_delete(Object* object); +BacktraceFrame* BacktraceFrame_new(void); + +extern const PanelClass BacktracePanel_class; +extern const ObjectClass BacktraceFrame_class; + +#endif diff --git a/CRT.c b/CRT.c index 9c8b841a2..1163bcd5d 100644 --- a/CRT.c +++ b/CRT.c @@ -222,6 +222,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_RATIO] = ColorPair(Magenta, Black), [ZRAM_COMPRESSED] = A_BOLD | ColorPair(Blue, Black), [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, Black), + [BACKTRACE_HEADER] = A_BOLD | A_ITALIC | A_BOLD | A_DIM | ColorPair(White, Black), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Black), @@ -340,6 +341,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_RATIO] = A_BOLD, [ZRAM_COMPRESSED] = A_NORMAL, [ZRAM_UNCOMPRESSED] = A_NORMAL, + [BACKTRACE_HEADER] = A_BOLD | A_ITALIC | A_BOLD | A_DIM, [DYNAMIC_GRAY] = A_DIM, [DYNAMIC_DARKGRAY] = A_DIM, [DYNAMIC_RED] = A_BOLD, @@ -458,6 +460,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_RATIO] = ColorPair(Magenta, White), [ZRAM_COMPRESSED] = ColorPair(Cyan, White), [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, White), + [BACKTRACE_HEADER] = A_BOLD | A_ITALIC | A_BOLD | A_DIM | ColorPair(Black, White), [DYNAMIC_GRAY] = ColorPair(Black, White), [DYNAMIC_DARKGRAY] = A_BOLD | ColorPair(Black, White), [DYNAMIC_RED] = ColorPair(Red, White), @@ -576,6 +579,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black), [ZRAM_COMPRESSED] = ColorPair(Cyan, Black), [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, Black), + [BACKTRACE_HEADER] = A_BOLD | A_ITALIC | A_BOLD | A_DIM | ColorPair(Black, Black), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Black), @@ -694,6 +698,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue), [ZRAM_COMPRESSED] = ColorPair(Cyan, Blue), [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, Blue), + [BACKTRACE_HEADER] = A_BOLD | A_ITALIC | A_BOLD | A_DIM | ColorPair(White, Blue), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Blue), @@ -810,6 +815,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_RATIO] = ColorPair(Magenta, Black), [ZRAM_COMPRESSED] = ColorPair(Blue, Black), [ZRAM_UNCOMPRESSED] = ColorPair(Yellow, Black), + [BACKTRACE_HEADER] = A_BOLD | A_ITALIC | A_BOLD | A_DIM | ColorPair(Cyan, Black), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Black), diff --git a/CRT.h b/CRT.h index 1b2b5742d..9c53d038a 100644 --- a/CRT.h +++ b/CRT.h @@ -145,6 +145,7 @@ typedef enum ColorElements_ { ZFS_RATIO, ZRAM_COMPRESSED, ZRAM_UNCOMPRESSED, + BACKTRACE_HEADER, DYNAMIC_GRAY, DYNAMIC_DARKGRAY, DYNAMIC_RED, diff --git a/Makefile.am b/Makefile.am index 386506c92..2a5af00e4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -165,6 +165,10 @@ myhtopheaders = \ Vector.h \ XUtils.h +if HAVE_BACKTRACE +myhtopheaders += BacktraceScreen.h +myhtopsources += BacktraceScreen.c +endif # Linux # ----- diff --git a/configure.ac b/configure.ac index 14872add8..1792935b0 100644 --- a/configure.ac +++ b/configure.ac @@ -787,6 +787,7 @@ esac if test "x$enable_backtrace" != xno; then AC_DEFINE([HAVE_BACKTRACE], [1], [Define if the backtrace feature is enabled]) fi +AM_CONDITIONAL([HAVE_BACKTRACE], [test x"$enable_backtrace" != xno]) if test "x$enable_backtrace" = xunwind-ptrace; then AC_MSG_CHECKING([if libunwind has unw_get_elf_filename]) diff --git a/darwin/Platform.c b/darwin/Platform.c index ba31a59f8..f5fef21b7 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -539,6 +539,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return true; } +void Platform_getBacktrace(Vector* frames, const Process* process, char **error) { + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +} + void Platform_getBattery(double* percent, ACPresence* isOnAC) { *percent = NAN; *isOnAC = AC_ERROR; diff --git a/darwin/Platform.h b/darwin/Platform.h index f67db8ff4..9ed5b4f46 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -76,6 +76,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char **error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); static inline void Platform_getHostname(char* buffer, size_t size) { diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index f3412ef9e..2729b5406 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -337,6 +337,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return true; } +void Platform_getBacktrace(Vector* frames, const Process* process, char **error) { + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +} + void Platform_getBattery(double* percent, ACPresence* isOnAC) { int life; size_t life_len = sizeof(life); diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index 606b004c0..2e18b5373 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -67,6 +67,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char **error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); static inline void Platform_getHostname(char* buffer, size_t size) { diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 153374b84..d6e54662b 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -371,6 +371,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return true; } +void Platform_getBacktrace(Vector* frames, const Process* process, char **error) { + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +} + void Platform_getBattery(double* percent, ACPresence* isOnAC) { int life; size_t life_len = sizeof(life); diff --git a/freebsd/Platform.h b/freebsd/Platform.h index c358d85d3..5a57c13f3 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -67,6 +67,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char **error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); static inline void Platform_getHostname(char* buffer, size_t size) { diff --git a/linux/Platform.c b/linux/Platform.c index c59be6840..d5e216fe7 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -14,15 +14,20 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include #include -#include +#include "BacktraceScreen.h" #include "BatteryMeter.h" #include "ClockMeter.h" #include "Compat.h" @@ -50,6 +55,7 @@ in the source distribution for its full text. #include "SysArchMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" +#include "Vector.h" #include "XUtils.h" #include "linux/GPUMeter.h" #include "linux/IOPriority.h" @@ -69,6 +75,14 @@ in the source distribution for its full text. #include #endif +#ifdef HAVE_LIBIBERTY +#include +#endif + +#ifdef HAVE_LIBUNWIND_PTRACE +#include +#endif + #ifdef HAVE_SENSORS_SENSORS_H #include "LibSensors.h" #endif @@ -677,6 +691,114 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return true; } +void Platform_getBacktrace(Vector* frames, const Process* process, char** error) { +#ifdef HAVE_LIBUNWIND_PTRACE + unw_addr_space_t addrSpace = unw_create_addr_space(&_UPT_accessors, 0); + if (!addrSpace) { + xAsprintf(error, "Unable to initialize libunwind."); + return; + } + + const pid_t pid = Process_getPid(process); + + if (pid <= 0) { + xAsprintf(error, "Unable to get the pid"); + goto addr_space_error; + } + + if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) { + int ptraceErrno = errno; + xAsprintf(error, "ptrace: %s (%d)", strerror(ptraceErrno), ptraceErrno); + goto addr_space_error; + } + + int waitStatus = 0; + if (wait(&waitStatus) == -1) { + int waitErrno = errno; + xAsprintf(error, "wait: %s (%d)", strerror(waitErrno), waitErrno); + goto ptrace_error; + } + + if (WIFSTOPPED(waitStatus) == 0) { + *error = xStrdup("The process chosen is not stopped correctly."); + goto ptrace_error; + } + + struct UPT_info* context = _UPT_create(pid); + if (!context) { + xAsprintf(error, "Unable to create the context of libunwind-ptrace"); + goto ptrace_error; + } + + unw_cursor_t cursor; + int ret = unw_init_remote(&cursor, addrSpace, context); + if (ret < 0) { + xAsprintf(error, "libunwind cursor: ret=%d", ret); + goto context_error; + } + + int index = 0; + do { + char procName[2048] = "?"; + unw_word_t offset; + unw_word_t pc; + + BacktraceFrame* frame = BacktraceFrame_new(); + frame->index = index; + if (unw_get_proc_name(&cursor, procName, sizeof(procName), &offset) == 0) { + ret = unw_get_reg(&cursor, UNW_REG_IP, &pc); + if (ret < 0) { + xAsprintf(error, "unable to get register rip : %d", ret); + break; + } + + frame->address = pc; + frame->offset = offset; + frame->isSignalFrame = unw_is_signal_frame(&cursor); + +#if defined(HAVE_LIBIBERTY) + char* demangledName = cplus_demangle(procName, + DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE | DMGL_RET_POSTFIX); + frame->demangleFunctionName = demangledName; +#endif + + frame->functionName = xStrdup(procName); + +#if defined(HAVE_LIBUNWIND_ELF_FILENAME) + unw_word_t offsetElfFileName; + char elfFileName[2048] = { 0 }; + if (unw_get_elf_filename(&cursor, elfFileName, sizeof(elfFileName), &offsetElfFileName) == 0) { + frame->objectPath = xStrdup(elfFileName); + + size_t n = 0; + char** splittedBySlash = String_split(frame->objectPath, '/', &n); + frame->objectName = xStrdup(splittedBySlash[n - 1]); + String_freeArray(splittedBySlash); + } +#endif + + } else { + frame->functionName = xStrdup("???"); + } + Vector_add(frames, (Object*)frame); + index++; + } while (unw_step(&cursor) > 0); + +context_error: + _UPT_destroy(context); + +ptrace_error: + ptrace(PTRACE_DETACH, pid, 0, 0); + +addr_space_error: + unw_destroy_addr_space(addrSpace); +#else /* !HAVE_LIBUNWIND_PTRACE */ + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +#endif +} + // Linux battery reading by Ian P. Hands (iphands@gmail.com, ihands@redhat.com). #define PROC_BATTERY_DIR PROCDIR "/acpi/battery" diff --git a/linux/Platform.h b/linux/Platform.h index e99d1a226..954648203 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -28,6 +28,7 @@ in the source distribution for its full text. #include "Settings.h" #include "SignalsPanel.h" #include "CommandLine.h" +#include "Vector.h" #include "generic/gettime.h" #include "generic/hostname.h" #include "generic/uname.h" @@ -86,6 +87,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char** error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); static inline void Platform_getHostname(char* buffer, size_t size) { diff --git a/netbsd/Platform.c b/netbsd/Platform.c index f458c239f..c0db9ab34 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -429,6 +429,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return true; } +void Platform_getBacktrace(Vector* frames, const Process* process, char **error) { + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +} + void Platform_getBattery(double* percent, ACPresence* isOnAC) { prop_dictionary_t dict, fields, props; prop_object_t device, class; diff --git a/netbsd/Platform.h b/netbsd/Platform.h index a543f52dd..d554d1b4f 100644 --- a/netbsd/Platform.h +++ b/netbsd/Platform.h @@ -73,6 +73,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char **error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); static inline void Platform_getHostname(char* buffer, size_t size) { diff --git a/openbsd/Platform.c b/openbsd/Platform.c index a8b5d212d..5d50f0369 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -342,6 +342,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return false; } +void Platform_getBacktrace(Vector* frames, const Process* process, char **error) { + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +} + static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) { for (int devn = 0;; devn++) { mib[2] = devn; diff --git a/openbsd/Platform.h b/openbsd/Platform.h index 339616c11..1124aae10 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -65,6 +65,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char **error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); static inline void Platform_getHostname(char* buffer, size_t size) { diff --git a/pcp/Platform.c b/pcp/Platform.c index 0db8b84b0..4b9a55469 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -771,6 +771,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return true; } +void Platform_getBacktrace(Vector* frames, const Process* process, char **error) { + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +} + void Platform_getFileDescriptors(double* used, double* max) { *used = NAN; *max = 65536; diff --git a/pcp/Platform.h b/pcp/Platform.h index f43ed54f2..f06dafabb 100644 --- a/pcp/Platform.h +++ b/pcp/Platform.h @@ -108,6 +108,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char **error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); void Platform_getHostname(char* buffer, size_t size); diff --git a/solaris/Platform.c b/solaris/Platform.c index 3934f7896..11e521d90 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -336,6 +336,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return false; } +void Platform_getBacktrace(Vector* frames, const Process* process, char **error) { + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +} + void Platform_getBattery(double* percent, ACPresence* isOnAC) { *percent = NAN; *isOnAC = AC_ERROR; diff --git a/solaris/Platform.h b/solaris/Platform.h index 1a31c2e7a..2bc2b76ef 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -92,6 +92,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char **error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); static inline void Platform_getHostname(char* buffer, size_t size) { diff --git a/unsupported/Platform.c b/unsupported/Platform.c index dbfddd916..e4cdc5f44 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -151,6 +151,12 @@ bool Platform_getNetworkIO(NetworkIOData* data) { return false; } +void Platform_getBacktrace(Vector* frames, const Process* process, char **error) { + (void)frames; + (void)process; + xAsprintf(error, "The backtrace screen is not implemented"); +} + void Platform_getBattery(double* percent, ACPresence* isOnAC) { *percent = NAN; *isOnAC = AC_ERROR; diff --git a/unsupported/Platform.h b/unsupported/Platform.h index c4cd06a04..fec5abbd0 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -61,6 +61,8 @@ bool Platform_getDiskIO(DiskIOData* data); bool Platform_getNetworkIO(NetworkIOData* data); +void Platform_getBacktrace(Vector* frames, const Process* process, char **error); + void Platform_getBattery(double* percent, ACPresence* isOnAC); void Platform_getHostname(char* buffer, size_t size);