aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Murashkin <iam@google.com>2013-10-30 21:09:27 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-10-30 21:09:28 +0000
commit1b84e0a3145f1497fc2259608d8830f371526ece (patch)
treec9365066beaf6eb71f2435641437de284cfe1df7
parent917045222a69021eb36aea96708649c709685509 (diff)
parentec79ef2e7b6b1d81266637ca0e002b5c0c5a789b (diff)
Merge "utils: Add ProcessCallStack to collect stack traces for all threads in a process" into klp-dev
-rw-r--r--include/utils/CallStack.h55
-rw-r--r--include/utils/Printer.h119
-rw-r--r--include/utils/ProcessCallStack.h79
-rw-r--r--libutils/Android.mk2
-rw-r--r--libutils/CallStack.cpp60
-rw-r--r--libutils/Printer.cpp155
-rw-r--r--libutils/ProcessCallStack.cpp256
7 files changed, 681 insertions, 45 deletions
diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h
index 61dc832e..20567517 100644
--- a/include/utils/CallStack.h
+++ b/include/utils/CallStack.h
@@ -17,50 +17,72 @@
#ifndef ANDROID_CALLSTACK_H
#define ANDROID_CALLSTACK_H
-#include <stdint.h>
-#include <sys/types.h>
-
+#include <android/log.h>
#include <utils/String8.h>
#include <corkscrew/backtrace.h>
-// ---------------------------------------------------------------------------
+#include <stdint.h>
+#include <sys/types.h>
namespace android {
-class CallStack
-{
+class Printer;
+
+// Collect/print the call stack (function, file, line) traces for a single thread.
+class CallStack {
public:
enum {
- MAX_DEPTH = 31
+ // Prune the lowest-most stack frames until we have at most MAX_DEPTH.
+ MAX_DEPTH = 31,
+ // Placeholder for specifying the current thread when updating the stack.
+ CURRENT_THREAD = -1,
};
+ // Create an empty call stack. No-op.
CallStack();
+ // Create a callstack with the current thread's stack trace.
+ // Immediately dump it to logcat using the given logtag.
CallStack(const char* logtag, int32_t ignoreDepth=1,
int32_t maxDepth=MAX_DEPTH);
+ // Copy the existing callstack (no other side effects).
CallStack(const CallStack& rhs);
~CallStack();
+ // Copy the existing callstack (no other side effects).
CallStack& operator = (const CallStack& rhs);
-
+
+ // Compare call stacks by their backtrace frame memory.
bool operator == (const CallStack& rhs) const;
bool operator != (const CallStack& rhs) const;
bool operator < (const CallStack& rhs) const;
bool operator >= (const CallStack& rhs) const;
bool operator > (const CallStack& rhs) const;
bool operator <= (const CallStack& rhs) const;
-
+
+ // Get the PC address for the stack frame specified by index.
const void* operator [] (int index) const;
-
+
+ // Reset the stack frames (same as creating an empty call stack).
void clear();
- void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH);
+ // Immediately collect the stack traces for the specified thread.
+ void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH, pid_t tid=CURRENT_THREAD);
- // Dump a stack trace to the log using the supplied logtag
- void dump(const char* logtag, const char* prefix = 0) const;
+ // Dump a stack trace to the log using the supplied logtag.
+ void log(const char* logtag,
+ android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0) const;
- // Return a string (possibly very long) containing the complete stack trace
+ // Dump a stack trace to the specified file descriptor.
+ void dump(int fd, int indent = 0, const char* prefix = 0) const;
+
+ // Return a string (possibly very long) containing the complete stack trace.
String8 toString(const char* prefix = 0) const;
-
+
+ // Dump a serialized representation of the stack trace to the specified printer.
+ void print(Printer& printer) const;
+
+ // Get the count of stack frames that are in this call stack.
size_t size() const { return mCount; }
private:
@@ -70,7 +92,4 @@ private:
}; // namespace android
-
-// ---------------------------------------------------------------------------
-
#endif // ANDROID_CALLSTACK_H
diff --git a/include/utils/Printer.h b/include/utils/Printer.h
new file mode 100644
index 00000000..bb662876
--- /dev/null
+++ b/include/utils/Printer.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_PRINTER_H
+#define ANDROID_PRINTER_H
+
+#include <android/log.h>
+
+namespace android {
+
+// Interface for printing to an arbitrary data stream
+class Printer {
+public:
+ // Print a new line specified by 'string'. \n is appended automatically.
+ // -- Assumes that the string has no new line in it.
+ virtual void printLine(const char* string = "") = 0;
+
+ // Print a new line specified by the format string. \n is appended automatically.
+ // -- Assumes that the resulting string has no new line in it.
+ virtual void printFormatLine(const char* format, ...) __attribute__((format (printf, 2, 3)));
+
+protected:
+ Printer();
+ virtual ~Printer();
+}; // class Printer
+
+// Print to logcat
+class LogPrinter : public Printer {
+public:
+ // Create a printer using the specified logcat and log priority
+ // - Unless ignoreBlankLines is false, print blank lines to logcat
+ // (Note that the default ALOG behavior is to ignore blank lines)
+ LogPrinter(const char* logtag,
+ android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0,
+ bool ignoreBlankLines = false);
+
+ // Print the specified line to logcat. No \n at the end is necessary.
+ virtual void printLine(const char* string);
+
+private:
+ void printRaw(const char* string);
+
+ const char* mLogTag;
+ android_LogPriority mPriority;
+ const char* mPrefix;
+ bool mIgnoreBlankLines;
+}; // class LogPrinter
+
+// Print to a file descriptor
+class FdPrinter : public Printer {
+public:
+ // Create a printer using the specified file descriptor.
+ // - Each line will be prefixed with 'indent' number of blank spaces.
+ // - In addition, each line will be prefixed with the 'prefix' string.
+ FdPrinter(int fd, unsigned int indent = 0, const char* prefix = 0);
+
+ // Print the specified line to the file descriptor. \n is appended automatically.
+ virtual void printLine(const char* string);
+
+private:
+ enum {
+ MAX_FORMAT_STRING = 20,
+ };
+
+ int mFd;
+ unsigned int mIndent;
+ const char* mPrefix;
+ char mFormatString[MAX_FORMAT_STRING];
+}; // class FdPrinter
+
+class String8;
+
+// Print to a String8
+class String8Printer : public Printer {
+public:
+ // Create a printer using the specified String8 as the target.
+ // - In addition, each line will be prefixed with the 'prefix' string.
+ // - target's memory lifetime must be a superset of this String8Printer.
+ String8Printer(String8* target, const char* prefix = 0);
+
+ // Append the specified line to the String8. \n is appended automatically.
+ virtual void printLine(const char* string);
+
+private:
+ String8* mTarget;
+ const char* mPrefix;
+}; // class String8Printer
+
+// Print to an existing Printer by adding a prefix to each line
+class PrefixPrinter : public Printer {
+public:
+ // Create a printer using the specified printer as the target.
+ PrefixPrinter(Printer& printer, const char* prefix);
+
+ // Print the line (prefixed with prefix) using the printer.
+ virtual void printLine(const char* string);
+
+private:
+ Printer& mPrinter;
+ const char* mPrefix;
+};
+
+}; // namespace android
+
+#endif // ANDROID_PRINTER_H
diff --git a/include/utils/ProcessCallStack.h b/include/utils/ProcessCallStack.h
new file mode 100644
index 00000000..4a868695
--- /dev/null
+++ b/include/utils/ProcessCallStack.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_PROCESS_CALLSTACK_H
+#define ANDROID_PROCESS_CALLSTACK_H
+
+#include <utils/CallStack.h>
+#include <android/log.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+#include <time.h>
+#include <sys/types.h>
+
+namespace android {
+
+class Printer;
+
+// Collect/print the call stack (function, file, line) traces for all threads in a process.
+class ProcessCallStack {
+public:
+ // Create an empty call stack. No-op.
+ ProcessCallStack();
+ // Copy the existing process callstack (no other side effects).
+ ProcessCallStack(const ProcessCallStack& rhs);
+ ~ProcessCallStack();
+
+ // Immediately collect the stack traces for all threads.
+ void update(int32_t maxDepth = CallStack::MAX_DEPTH);
+
+ // Print all stack traces to the log using the supplied logtag.
+ void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG,
+ const char* prefix = 0) const;
+
+ // Dump all stack traces to the specified file descriptor.
+ void dump(int fd, int indent = 0, const char* prefix = 0) const;
+
+ // Return a string (possibly very long) containing all the stack traces.
+ String8 toString(const char* prefix = 0) const;
+
+ // Dump a serialized representation of all the stack traces to the specified printer.
+ void print(Printer& printer) const;
+
+ // Get the number of threads whose stack traces were collected.
+ size_t size() const;
+
+private:
+ void printInternal(Printer& printer, Printer& csPrinter) const;
+
+ // Reset the process's stack frames and metadata.
+ void clear();
+
+ struct ThreadInfo {
+ CallStack callStack;
+ String8 threadName;
+ };
+
+ // tid -> ThreadInfo
+ KeyedVector<pid_t, ThreadInfo> mThreadMap;
+ // Time that update() was last called
+ struct tm mTimeUpdated;
+};
+
+}; // namespace android
+
+#endif // ANDROID_PROCESS_CALLSTACK_H
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 7e6b1be4..720443e8 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 e60f5d8e..4ceaa7c9 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 00000000..b062ef0a
--- /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 00000000..ed35237a
--- /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