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 <utils/ProcessCallStack.h>
     21 
     22 #include <dirent.h>
     23 #include <unistd.h>
     24 
     25 #include <memory>
     26 
     27 #include <utils/Printer.h>
     28 
     29 namespace android {
     30 
     31 enum {
     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 
     40 static const char* CALL_STACK_PREFIX = "  ";
     41 static const char* PATH_THREAD_NAME = "/proc/self/task/%d/comm";
     42 static const char* PATH_SELF_TASK = "/proc/self/task";
     43 
     44 static 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 
     71 static void dumpProcessFooter(Printer& printer, pid_t pid) {
     72     printer.printLine();
     73     printer.printFormatLine("----- end %d -----", pid);
     74     printer.printLine();
     75 }
     76 
     77 static 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     if (procName == NULL) {
     92         // Reading /proc/self/task/%d/comm failed due to a race
     93         return String8::format("[err-unknown-tid-%d]", tid);
     94     }
     95 
     96     // Strip ending newline
     97     strtok(procName, "\n");
     98 
     99     return String8(procName);
    100 }
    101 
    102 static String8 getTimeString(struct tm tm) {
    103     char timestr[MAX_TIME_STRING];
    104     // i.e. '2013-10-22 14:42:05'
    105     strftime(timestr, sizeof(timestr), "%F %T", &tm);
    106 
    107     return String8(timestr);
    108 }
    109 
    110 /*
    111  * Implementation of ProcessCallStack
    112  */
    113 ProcessCallStack::ProcessCallStack() {
    114 }
    115 
    116 ProcessCallStack::ProcessCallStack(const ProcessCallStack& rhs) :
    117         mThreadMap(rhs.mThreadMap),
    118         mTimeUpdated(rhs.mTimeUpdated) {
    119 }
    120 
    121 ProcessCallStack::~ProcessCallStack() {
    122 }
    123 
    124 void ProcessCallStack::clear() {
    125     mThreadMap.clear();
    126     mTimeUpdated = tm();
    127 }
    128 
    129 void ProcessCallStack::update() {
    130     std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);
    131     if (dp == NULL) {
    132         ALOGE("%s: Failed to update the process's call stacks: %s",
    133               __FUNCTION__, strerror(errno));
    134         return;
    135     }
    136 
    137     pid_t selfPid = getpid();
    138 
    139     clear();
    140 
    141     // Get current time.
    142     {
    143         time_t t = time(NULL);
    144         struct tm tm;
    145         localtime_r(&t, &tm);
    146 
    147         mTimeUpdated = tm;
    148     }
    149 
    150     /*
    151      * Each tid is a directory inside of /proc/self/task
    152      * - Read every file in directory => get every tid
    153      */
    154     dirent* ep;
    155     while ((ep = readdir(dp.get())) != NULL) {
    156         pid_t tid = -1;
    157         sscanf(ep->d_name, "%d", &tid);
    158 
    159         if (tid < 0) {
    160             // Ignore '.' and '..'
    161             ALOGV("%s: Failed to read tid from %s/%s",
    162                   __FUNCTION__, PATH_SELF_TASK, ep->d_name);
    163             continue;
    164         }
    165 
    166         ssize_t idx = mThreadMap.add(tid, ThreadInfo());
    167         if (idx < 0) { // returns negative error value on error
    168             ALOGE("%s: Failed to add new ThreadInfo: %s",
    169                   __FUNCTION__, strerror(-idx));
    170             continue;
    171         }
    172 
    173         ThreadInfo& threadInfo = mThreadMap.editValueAt(static_cast<size_t>(idx));
    174 
    175         /*
    176          * Ignore CallStack::update and ProcessCallStack::update for current thread
    177          * - Every other thread doesn't need this since we call update off-thread
    178          */
    179         int ignoreDepth = (selfPid == tid) ? IGNORE_DEPTH_CURRENT_THREAD : 0;
    180 
    181         // Update thread's call stacks
    182         threadInfo.callStack.update(ignoreDepth, 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, threadInfo.callStack.size());
    189     }
    190 }
    191 
    192 void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
    193                            const char* prefix) const {
    194     LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);
    195     print(printer);
    196 }
    197 
    198 void ProcessCallStack::print(Printer& printer) const {
    199     /*
    200      * Print the header/footer with the regular printer.
    201      * Print the callstack with an additional two spaces as the prefix for legibility.
    202      */
    203     PrefixPrinter csPrinter(printer, CALL_STACK_PREFIX);
    204     printInternal(printer, csPrinter);
    205 }
    206 
    207 void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
    208     dumpProcessHeader(printer, getpid(),
    209                       getTimeString(mTimeUpdated).string());
    210 
    211     for (size_t i = 0; i < mThreadMap.size(); ++i) {
    212         pid_t tid = mThreadMap.keyAt(i);
    213         const ThreadInfo& threadInfo = mThreadMap.valueAt(i);
    214         const String8& threadName = threadInfo.threadName;
    215 
    216         printer.printLine("");
    217         printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
    218 
    219         threadInfo.callStack.print(csPrinter);
    220     }
    221 
    222     dumpProcessFooter(printer, getpid());
    223 }
    224 
    225 void ProcessCallStack::dump(int fd, int indent, const char* prefix) const {
    226 
    227     if (indent < 0) {
    228         ALOGW("%s: Bad indent (%d)", __FUNCTION__, indent);
    229         return;
    230     }
    231 
    232     FdPrinter printer(fd, static_cast<unsigned int>(indent), prefix);
    233     print(printer);
    234 }
    235 
    236 String8 ProcessCallStack::toString(const char* prefix) const {
    237 
    238     String8 dest;
    239     String8Printer printer(&dest, prefix);
    240     print(printer);
    241 
    242     return dest;
    243 }
    244 
    245 size_t ProcessCallStack::size() const {
    246     return mThreadMap.size();
    247 }
    248 
    249 }; //namespace android
    250