ProcessCallStack.cpp revision ec79ef2e7b6b1d81266637ca0e002b5c0c5a789b
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "ProcessCallStack"
18// #define LOG_NDEBUG 0
19
20#include <string.h>
21#include <stdio.h>
22#include <dirent.h>
23
24#include <utils/Log.h>
25#include <utils/Errors.h>
26#include <utils/ProcessCallStack.h>
27#include <utils/Printer.h>
28
29namespace android {
30
31enum {
32    // Max sizes for various dynamically generated strings
33    MAX_TIME_STRING = 64,
34    MAX_PROC_PATH = 1024,
35
36    // Dump related prettiness constants
37    IGNORE_DEPTH_CURRENT_THREAD = 2,
38};
39
40static const char* CALL_STACK_PREFIX = "  ";
41static const char* PATH_THREAD_NAME = "/proc/self/task/%d/comm";
42static const char* PATH_SELF_TASK = "/proc/self/task";
43
44static void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {
45    if (timeStr == NULL) {
46        ALOGW("%s: timeStr was NULL", __FUNCTION__);
47        return;
48    }
49
50    char path[PATH_MAX];
51    char procNameBuf[MAX_PROC_PATH];
52    char* procName = NULL;
53    FILE* fp;
54
55    snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
56    if ((fp = fopen(path, "r"))) {
57        procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
58        fclose(fp);
59    }
60
61    if (!procName) {
62        procName = const_cast<char*>("<unknown>");
63    }
64
65    printer.printLine();
66    printer.printLine();
67    printer.printFormatLine("----- pid %d at %s -----", pid, timeStr);
68    printer.printFormatLine("Cmd line: %s", procName);
69}
70
71static void dumpProcessFooter(Printer& printer, pid_t pid) {
72    printer.printLine();
73    printer.printFormatLine("----- end %d -----", pid);
74    printer.printLine();
75}
76
77static String8 getThreadName(pid_t tid) {
78    char path[PATH_MAX];
79    char* procName = NULL;
80    char procNameBuf[MAX_PROC_PATH];
81    FILE* fp;
82
83    snprintf(path, sizeof(path), PATH_THREAD_NAME, tid);
84    if ((fp = fopen(path, "r"))) {
85        procName = fgets(procNameBuf, sizeof(procNameBuf), fp);
86        fclose(fp);
87    } else {
88        ALOGE("%s: Failed to open %s", __FUNCTION__, path);
89    }
90
91    // Strip ending newline
92    strtok(procName, "\n");
93
94    return String8(procName);
95}
96
97static String8 getTimeString(struct tm tm) {
98    char timestr[MAX_TIME_STRING];
99    // i.e. '2013-10-22 14:42:05'
100    strftime(timestr, sizeof(timestr), "%F %T", &tm);
101
102    return String8(timestr);
103}
104
105/*
106 * Implementation of ProcessCallStack
107 */
108ProcessCallStack::ProcessCallStack() {
109}
110
111ProcessCallStack::ProcessCallStack(const ProcessCallStack& rhs) :
112        mThreadMap(rhs.mThreadMap),
113        mTimeUpdated(rhs.mTimeUpdated) {
114}
115
116ProcessCallStack::~ProcessCallStack() {
117}
118
119void ProcessCallStack::clear() {
120    mThreadMap.clear();
121    mTimeUpdated = tm();
122}
123
124void ProcessCallStack::update(int32_t maxDepth) {
125    DIR *dp;
126    struct dirent *ep;
127    struct dirent entry;
128
129    dp = opendir(PATH_SELF_TASK);
130    if (dp == NULL) {
131        ALOGE("%s: Failed to update the process's call stacks (errno = %d, '%s')",
132              __FUNCTION__, errno, strerror(errno));
133        return;
134    }
135
136    pid_t selfPid = getpid();
137
138    clear();
139
140    // Get current time.
141    {
142        time_t t = time(NULL);
143        struct tm tm;
144        localtime_r(&t, &tm);
145
146        mTimeUpdated = tm;
147    }
148
149    /*
150     * Each tid is a directory inside of /proc/self/task
151     * - Read every file in directory => get every tid
152     */
153    int code;
154    while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
155        pid_t tid = -1;
156        sscanf(ep->d_name, "%d", &tid);
157
158        if (tid < 0) {
159            // Ignore '.' and '..'
160            ALOGV("%s: Failed to read tid from %s/%s",
161                  __FUNCTION__, PATH_SELF_TASK, ep->d_name);
162            continue;
163        }
164
165        ssize_t idx = mThreadMap.add(tid, ThreadInfo());
166        if (idx < 0) { // returns negative error value on error
167            ALOGE("%s: Failed to add new ThreadInfo (errno = %zd, '%s')",
168                  __FUNCTION__, idx, strerror(-idx));
169            continue;
170        }
171
172        ThreadInfo& threadInfo = mThreadMap.editValueAt(static_cast<size_t>(idx));
173
174        /*
175         * Ignore CallStack::update and ProcessCallStack::update for current thread
176         * - Every other thread doesn't need this since we call update off-thread
177         */
178        int ignoreDepth = (selfPid == tid) ? IGNORE_DEPTH_CURRENT_THREAD : 0;
179
180        // Update thread's call stacks
181        CallStack& cs = threadInfo.callStack;
182        cs.update(ignoreDepth, maxDepth, tid);
183
184        // Read/save thread name
185        threadInfo.threadName = getThreadName(tid);
186
187        ALOGV("%s: Got call stack for tid %d (size %zu)",
188              __FUNCTION__, tid, cs.size());
189    }
190    if (code != 0) { // returns positive error value on error
191        ALOGE("%s: Failed to readdir from %s (errno = %d, '%s')",
192              __FUNCTION__, PATH_SELF_TASK, -code, strerror(code));
193    }
194
195    closedir(dp);
196}
197
198void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
199                           const char* prefix) const {
200    LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
201    print(printer);
202}
203
204void ProcessCallStack::print(Printer& printer) const {
205    /*
206     * Print the header/footer with the regular printer.
207     * Print the callstack with an additional two spaces as the prefix for legibility.
208     */
209    PrefixPrinter csPrinter(printer, CALL_STACK_PREFIX);
210    printInternal(printer, csPrinter);
211}
212
213void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
214    dumpProcessHeader(printer, getpid(),
215                      getTimeString(mTimeUpdated).string());
216
217    for (size_t i = 0; i < mThreadMap.size(); ++i) {
218        pid_t tid = mThreadMap.keyAt(i);
219        const ThreadInfo& threadInfo = mThreadMap.valueAt(i);
220        const CallStack& cs = threadInfo.callStack;
221        const String8& threadName = threadInfo.threadName;
222
223        printer.printLine("");
224        printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
225
226        cs.print(csPrinter);
227    }
228
229    dumpProcessFooter(printer, getpid());
230}
231
232void ProcessCallStack::dump(int fd, int indent, const char* prefix) const {
233
234    if (indent < 0) {
235        ALOGW("%s: Bad indent (%d)", __FUNCTION__, indent);
236        return;
237    }
238
239    FdPrinter printer(fd, static_cast<unsigned int>(indent), prefix);
240    print(printer);
241}
242
243String8 ProcessCallStack::toString(const char* prefix) const {
244
245    String8 dest;
246    String8Printer printer(&dest, prefix);
247    print(printer);
248
249    return dest;
250}
251
252size_t ProcessCallStack::size() const {
253    return mThreadMap.size();
254}
255
256}; //namespace android
257