am 39f18c9e: am 1b84e0a3: Merge "utils: Add ProcessCallStack to collect stack traces for all threads in a process" into klp-dev

* commit '39f18c9ece973a0eaff7e982a120de63f31ee5c0':
  utils: Add ProcessCallStack to collect stack traces for all threads in a process
This commit is contained in:
Igor Murashkin 2013-10-30 14:18:46 -07:00 committed by Android Git Automerger
commit de2eae31b3
7 changed files with 681 additions and 45 deletions

View File

@ -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

119
include/utils/Printer.h Normal file
View File

@ -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

View File

@ -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

View File

@ -26,6 +26,8 @@ commonSources:= \
LinearAllocator.cpp \
LinearTransform.cpp \
Log.cpp \
Printer.cpp \
ProcessCallStack.cpp \
PropertyMap.cpp \
RefBase.cpp \
SharedBuffer.cpp \

View File

@ -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

155
libutils/Printer.cpp Normal file
View File

@ -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

View File

@ -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