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