You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
6.7 KiB
249 lines
6.7 KiB
/* |
|
* 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 <utils/ProcessCallStack.h> |
|
|
|
#include <dirent.h> |
|
#include <unistd.h> |
|
|
|
#include <memory> |
|
|
|
#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); |
|
} |
|
|
|
if (procName == NULL) { |
|
// Reading /proc/self/task/%d/comm failed due to a race |
|
return String8::format("[err-unknown-tid-%d]", tid); |
|
} |
|
|
|
// 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() { |
|
std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir); |
|
if (dp == NULL) { |
|
ALOGE("%s: Failed to update the process's call stacks: %s", |
|
__FUNCTION__, 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 |
|
*/ |
|
dirent* ep; |
|
while ((ep = readdir(dp.get())) != 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: %s", |
|
__FUNCTION__, 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 |
|
threadInfo.callStack.update(ignoreDepth, tid); |
|
|
|
// Read/save thread name |
|
threadInfo.threadName = getThreadName(tid); |
|
|
|
ALOGV("%s: Got call stack for tid %d (size %zu)", |
|
__FUNCTION__, tid, threadInfo.callStack.size()); |
|
} |
|
} |
|
|
|
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 String8& threadName = threadInfo.threadName; |
|
|
|
printer.printLine(""); |
|
printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid); |
|
|
|
threadInfo.callStack.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
|
|
|