summaryrefslogtreecommitdiffstats
path: root/libutils
diff options
context:
space:
mode:
authorIgor Murashkin <iam@google.com>2013-10-24 17:09:15 -0700
committerIgor Murashkin <iam@google.com>2013-10-25 19:24:56 -0700
commitec79ef2e7b6b1d81266637ca0e002b5c0c5a789b (patch)
tree309ff8a328db5ccd522995abe3614959cfded1c6 /libutils
parenta9e453f1b552699f69dca19599c7624a581089bd (diff)
downloadsystem_core-ec79ef2e7b6b1d81266637ca0e002b5c0c5a789b.zip
system_core-ec79ef2e7b6b1d81266637ca0e002b5c0c5a789b.tar.gz
system_core-ec79ef2e7b6b1d81266637ca0e002b5c0c5a789b.tar.bz2
utils: Add ProcessCallStack to collect stack traces for all threads in a process
- Also add a Printer class (print lines to logcat, fd, or strings) Bug: 11324229 Change-Id: I78435ed49aa196a0efb45bf9b2d58b62c41737d3
Diffstat (limited to 'libutils')
-rw-r--r--libutils/Android.mk2
-rw-r--r--libutils/CallStack.cpp60
-rw-r--r--libutils/Printer.cpp155
-rw-r--r--libutils/ProcessCallStack.cpp256
4 files changed, 446 insertions, 27 deletions
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 7e6b1be..720443e 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -26,6 +26,8 @@ commonSources:= \
LinearAllocator.cpp \
LinearTransform.cpp \
Log.cpp \
+ Printer.cpp \
+ ProcessCallStack.cpp \
PropertyMap.cpp \
RefBase.cpp \
SharedBuffer.cpp \
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index e60f5d8..4ceaa7c 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,14 +16,12 @@
#define LOG_TAG "CallStack"
-#include <string.h>
-
-#include <utils/Log.h>
-#include <utils/Errors.h>
#include <utils/CallStack.h>
+#include <utils/Printer.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
#include <corkscrew/backtrace.h>
-/*****************************************************************************/
namespace android {
CallStack::CallStack() :
@@ -31,8 +29,8 @@ CallStack::CallStack() :
}
CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) {
- this->update(ignoreDepth+1, maxDepth);
- this->dump(logtag);
+ this->update(ignoreDepth+1, maxDepth, CURRENT_THREAD);
+ this->log(logtag);
}
CallStack::CallStack(const CallStack& rhs) :
@@ -93,31 +91,44 @@ void CallStack::clear() {
mCount = 0;
}
-void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) {
+void CallStack::update(int32_t ignoreDepth, int32_t maxDepth, pid_t tid) {
if (maxDepth > MAX_DEPTH) {
maxDepth = MAX_DEPTH;
}
- ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
+ ssize_t count;
+
+ if (tid >= 0) {
+ count = unwind_backtrace_thread(tid, mStack, ignoreDepth + 1, maxDepth);
+ } else if (tid == CURRENT_THREAD) {
+ count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth);
+ } else {
+ ALOGE("%s: Invalid tid specified (%d)", __FUNCTION__, tid);
+ count = 0;
+ }
+
mCount = count > 0 ? count : 0;
}
-void CallStack::dump(const char* logtag, const char* prefix) const {
- backtrace_symbol_t symbols[mCount];
+void CallStack::log(const char* logtag, android_LogPriority priority, const char* prefix) const {
+ LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
+ print(printer);
+}
- get_backtrace_symbols(mStack, mCount, symbols);
- for (size_t i = 0; i < mCount; i++) {
- char line[MAX_BACKTRACE_LINE_LENGTH];
- format_backtrace_line(i, &mStack[i], &symbols[i],
- line, MAX_BACKTRACE_LINE_LENGTH);
- ALOG(LOG_DEBUG, logtag, "%s%s",
- prefix ? prefix : "",
- line);
- }
- free_backtrace_symbols(symbols, mCount);
+void CallStack::dump(int fd, int indent, const char* prefix) const {
+ FdPrinter printer(fd, indent, prefix);
+ print(printer);
}
String8 CallStack::toString(const char* prefix) const {
String8 str;
+
+ String8Printer printer(&str, prefix);
+ print(printer);
+
+ return str;
+}
+
+void CallStack::print(Printer& printer) const {
backtrace_symbol_t symbols[mCount];
get_backtrace_symbols(mStack, mCount, symbols);
@@ -125,14 +136,9 @@ String8 CallStack::toString(const char* prefix) const {
char line[MAX_BACKTRACE_LINE_LENGTH];
format_backtrace_line(i, &mStack[i], &symbols[i],
line, MAX_BACKTRACE_LINE_LENGTH);
- if (prefix) {
- str.append(prefix);
- }
- str.append(line);
- str.append("\n");
+ printer.printLine(line);
}
free_backtrace_symbols(symbols, mCount);
- return str;
}
}; // namespace android
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
new file mode 100644
index 0000000..b062ef0
--- /dev/null
+++ b/libutils/Printer.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "Printer"
+// #define LOG_NDEBUG 0
+
+#include <utils/Printer.h>
+#include <utils/String8.h>
+#include <utils/Log.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __BIONIC__
+#define fdprintf dprintf
+#endif
+
+namespace android {
+
+/*
+ * Implementation of Printer
+ */
+Printer::Printer() {
+ // Intentionally left empty
+}
+
+Printer::~Printer() {
+ // Intentionally left empty
+}
+
+void Printer::printFormatLine(const char* format, ...) {
+ va_list arglist;
+ va_start(arglist, format);
+
+ char* formattedString;
+ if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error
+ ALOGE("%s: Failed to format string", __FUNCTION__);
+ return;
+ }
+ va_end(arglist);
+
+ printLine(formattedString);
+ free(formattedString);
+}
+
+/*
+ * Implementation of LogPrinter
+ */
+LogPrinter::LogPrinter(const char* logtag,
+ android_LogPriority priority,
+ const char* prefix,
+ bool ignoreBlankLines) :
+ mLogTag(logtag),
+ mPriority(priority),
+ mPrefix(prefix ?: ""),
+ mIgnoreBlankLines(ignoreBlankLines) {
+}
+
+void LogPrinter::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ }
+
+ if (mIgnoreBlankLines || (*string)) {
+ // Simple case: Line is not blank, or we don't care about printing blank lines
+ printRaw(string);
+ } else {
+ // Force logcat to print empty lines by adding prefixing with a space
+ printRaw(" ");
+ }
+}
+
+void LogPrinter::printRaw(const char* string) {
+ __android_log_print(mPriority, mLogTag, "%s%s", mPrefix, string);
+}
+
+
+/*
+ * Implementation of FdPrinter
+ */
+FdPrinter::FdPrinter(int fd, unsigned int indent, const char* prefix) :
+ mFd(fd), mIndent(indent), mPrefix(prefix ?: "") {
+
+ if (fd < 0) {
+ ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, fd);
+ }
+
+ // <indent><prefix><line> -- e.g. '%-4s%s\n' for indent=4
+ snprintf(mFormatString, sizeof(mFormatString), "%%-%us%%s\n", mIndent);
+}
+
+void FdPrinter::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ } else if (mFd < 0) {
+ ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, mFd);
+ return;
+ }
+
+ fdprintf(mFd, mFormatString, mPrefix, string);
+}
+
+/*
+ * Implementation of String8Printer
+ */
+String8Printer::String8Printer(String8* target, const char* prefix) :
+ mTarget(target),
+ mPrefix(prefix ?: "") {
+
+ if (target == NULL) {
+ ALOGW("%s: Target string was NULL", __FUNCTION__);
+ }
+}
+
+void String8Printer::printLine(const char* string) {
+ if (string == NULL) {
+ ALOGW("%s: NULL string passed in", __FUNCTION__);
+ return;
+ } else if (mTarget == NULL) {
+ ALOGW("%s: Target string was NULL", __FUNCTION__);
+ return;
+ }
+
+ mTarget->append(string);
+ mTarget->append("\n");
+}
+
+/*
+ * Implementation of PrefixPrinter
+ */
+PrefixPrinter::PrefixPrinter(Printer& printer, const char* prefix) :
+ mPrinter(printer), mPrefix(prefix ?: "") {
+}
+
+void PrefixPrinter::printLine(const char* string) {
+ mPrinter.printFormatLine("%s%s", mPrefix, string);
+}
+
+}; //namespace android
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
new file mode 100644
index 0000000..ed35237
--- /dev/null
+++ b/libutils/ProcessCallStack.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "ProcessCallStack"
+// #define LOG_NDEBUG 0
+
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/ProcessCallStack.h>
+#include <utils/Printer.h>
+
+namespace android {
+
+enum {
+ // Max sizes for various dynamically generated strings
+ MAX_TIME_STRING = 64,
+ MAX_PROC_PATH = 1024,
+
+ // Dump related prettiness constants
+ IGNORE_DEPTH_CURRENT_THREAD = 2,
+};
+
+static const char* CALL_STACK_PREFIX = " ";
+static const char* PATH_THREAD_NAME = "/proc/self/task/%d/comm";
+static const char* PATH_SELF_TASK = "/proc/self/task";
+
+static void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {
+ if (timeStr == NULL) {
+ ALOGW("%s: timeStr was NULL", __FUNCTION__);
+ return;
+ }
+
+ char path[PATH_MAX];
+ char procNameBuf[MAX_PROC_PATH];
+ char* procName = NULL;
+ FILE* fp;
+
+ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+ if ((fp = fopen(path, "r"))) {
+ procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
+ fclose(fp);
+ }
+
+ if (!procName) {
+ procName = const_cast<char*>("<unknown>");
+ }
+
+ printer.printLine();
+ printer.printLine();
+ printer.printFormatLine("----- pid %d at %s -----", pid, timeStr);
+ printer.printFormatLine("Cmd line: %s", procName);
+}
+
+static void dumpProcessFooter(Printer& printer, pid_t pid) {
+ printer.printLine();
+ printer.printFormatLine("----- end %d -----", pid);
+ printer.printLine();
+}
+
+static String8 getThreadName(pid_t tid) {
+ char path[PATH_MAX];
+ char* procName = NULL;
+ char procNameBuf[MAX_PROC_PATH];
+ FILE* fp;
+
+ snprintf(path, sizeof(path), PATH_THREAD_NAME, tid);
+ if ((fp = fopen(path, "r"))) {
+ procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
+ fclose(fp);
+ } else {
+ ALOGE("%s: Failed to open %s", __FUNCTION__, path);
+ }
+
+ // Strip ending newline
+ strtok(procName, "\n");
+
+ return String8(procName);
+}
+
+static String8 getTimeString(struct tm tm) {
+ char timestr[MAX_TIME_STRING];
+ // i.e. '2013-10-22 14:42:05'
+ strftime(timestr, sizeof(timestr), "%F %T", &tm);
+
+ return String8(timestr);
+}
+
+/*
+ * Implementation of ProcessCallStack
+ */
+ProcessCallStack::ProcessCallStack() {
+}
+
+ProcessCallStack::ProcessCallStack(const ProcessCallStack& rhs) :
+ mThreadMap(rhs.mThreadMap),
+ mTimeUpdated(rhs.mTimeUpdated) {
+}
+
+ProcessCallStack::~ProcessCallStack() {
+}
+
+void ProcessCallStack::clear() {
+ mThreadMap.clear();
+ mTimeUpdated = tm();
+}
+
+void ProcessCallStack::update(int32_t maxDepth) {
+ DIR *dp;
+ struct dirent *ep;
+ struct dirent entry;
+
+ dp = opendir(PATH_SELF_TASK);
+ if (dp == NULL) {
+ ALOGE("%s: Failed to update the process's call stacks (errno = %d, '%s')",
+ __FUNCTION__, errno, strerror(errno));
+ return;
+ }
+
+ pid_t selfPid = getpid();
+
+ clear();
+
+ // Get current time.
+ {
+ time_t t = time(NULL);
+ struct tm tm;
+ localtime_r(&t, &tm);
+
+ mTimeUpdated = tm;
+ }
+
+ /*
+ * Each tid is a directory inside of /proc/self/task
+ * - Read every file in directory => get every tid
+ */
+ int code;
+ while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
+ pid_t tid = -1;
+ sscanf(ep->d_name, "%d", &tid);
+
+ if (tid < 0) {
+ // Ignore '.' and '..'
+ ALOGV("%s: Failed to read tid from %s/%s",
+ __FUNCTION__, PATH_SELF_TASK, ep->d_name);
+ continue;
+ }
+
+ ssize_t idx = mThreadMap.add(tid, ThreadInfo());
+ if (idx < 0) { // returns negative error value on error
+ ALOGE("%s: Failed to add new ThreadInfo (errno = %zd, '%s')",
+ __FUNCTION__, idx, strerror(-idx));
+ continue;
+ }
+
+ ThreadInfo& threadInfo = mThreadMap.editValueAt(static_cast<size_t>(idx));
+
+ /*
+ * Ignore CallStack::update and ProcessCallStack::update for current thread
+ * - Every other thread doesn't need this since we call update off-thread
+ */
+ int ignoreDepth = (selfPid == tid) ? IGNORE_DEPTH_CURRENT_THREAD : 0;
+
+ // Update thread's call stacks
+ CallStack& cs = threadInfo.callStack;
+ cs.update(ignoreDepth, maxDepth, tid);
+
+ // Read/save thread name
+ threadInfo.threadName = getThreadName(tid);
+
+ ALOGV("%s: Got call stack for tid %d (size %zu)",
+ __FUNCTION__, tid, cs.size());
+ }
+ if (code != 0) { // returns positive error value on error
+ ALOGE("%s: Failed to readdir from %s (errno = %d, '%s')",
+ __FUNCTION__, PATH_SELF_TASK, -code, strerror(code));
+ }
+
+ closedir(dp);
+}
+
+void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
+ const char* prefix) const {
+ LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
+ print(printer);
+}
+
+void ProcessCallStack::print(Printer& printer) const {
+ /*
+ * Print the header/footer with the regular printer.
+ * Print the callstack with an additional two spaces as the prefix for legibility.
+ */
+ PrefixPrinter csPrinter(printer, CALL_STACK_PREFIX);
+ printInternal(printer, csPrinter);
+}
+
+void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
+ dumpProcessHeader(printer, getpid(),
+ getTimeString(mTimeUpdated).string());
+
+ for (size_t i = 0; i < mThreadMap.size(); ++i) {
+ pid_t tid = mThreadMap.keyAt(i);
+ const ThreadInfo& threadInfo = mThreadMap.valueAt(i);
+ const CallStack& cs = threadInfo.callStack;
+ const String8& threadName = threadInfo.threadName;
+
+ printer.printLine("");
+ printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
+
+ cs.print(csPrinter);
+ }
+
+ dumpProcessFooter(printer, getpid());
+}
+
+void ProcessCallStack::dump(int fd, int indent, const char* prefix) const {
+
+ if (indent < 0) {
+ ALOGW("%s: Bad indent (%d)", __FUNCTION__, indent);
+ return;
+ }
+
+ FdPrinter printer(fd, static_cast<unsigned int>(indent), prefix);
+ print(printer);
+}
+
+String8 ProcessCallStack::toString(const char* prefix) const {
+
+ String8 dest;
+ String8Printer printer(&dest, prefix);
+ print(printer);
+
+ return dest;
+}
+
+size_t ProcessCallStack::size() const {
+ return mThreadMap.size();
+}
+
+}; //namespace android