diff options
author | Igor Murashkin | 2013-10-24 19:09:15 -0500 |
---|---|---|
committer | Igor Murashkin | 2013-10-25 21:24:56 -0500 |
commit | ec79ef2e7b6b1d81266637ca0e002b5c0c5a789b (patch) | |
tree | 309ff8a328db5ccd522995abe3614959cfded1c6 | |
parent | a9e453f1b552699f69dca19599c7624a581089bd (diff) | |
download | platform-system-core-ec79ef2e7b6b1d81266637ca0e002b5c0c5a789b.tar.gz platform-system-core-ec79ef2e7b6b1d81266637ca0e002b5c0c5a789b.tar.xz platform-system-core-ec79ef2e7b6b1d81266637ca0e002b5c0c5a789b.zip |
utils: Add ProcessCallStack to collect stack traces for all threads in a process
- Also add a Printer class (print lines to logcat, fd, or strings)
Bug: 11324229
Change-Id: I78435ed49aa196a0efb45bf9b2d58b62c41737d3
-rw-r--r-- | include/utils/CallStack.h | 55 | ||||
-rw-r--r-- | include/utils/Printer.h | 119 | ||||
-rw-r--r-- | include/utils/ProcessCallStack.h | 79 | ||||
-rw-r--r-- | libutils/Android.mk | 2 | ||||
-rw-r--r-- | libutils/CallStack.cpp | 60 | ||||
-rw-r--r-- | libutils/Printer.cpp | 155 | ||||
-rw-r--r-- | libutils/ProcessCallStack.cpp | 256 |
7 files changed, 681 insertions, 45 deletions
diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h index 61dc832ea..205675173 100644 --- a/include/utils/CallStack.h +++ b/include/utils/CallStack.h | |||
@@ -17,50 +17,72 @@ | |||
17 | #ifndef ANDROID_CALLSTACK_H | 17 | #ifndef ANDROID_CALLSTACK_H |
18 | #define ANDROID_CALLSTACK_H | 18 | #define ANDROID_CALLSTACK_H |
19 | 19 | ||
20 | #include <stdint.h> | 20 | #include <android/log.h> |
21 | #include <sys/types.h> | ||
22 | |||
23 | #include <utils/String8.h> | 21 | #include <utils/String8.h> |
24 | #include <corkscrew/backtrace.h> | 22 | #include <corkscrew/backtrace.h> |
25 | 23 | ||
26 | // --------------------------------------------------------------------------- | 24 | #include <stdint.h> |
25 | #include <sys/types.h> | ||
27 | 26 | ||
28 | namespace android { | 27 | namespace android { |
29 | 28 | ||
30 | class CallStack | 29 | class Printer; |
31 | { | 30 | |
31 | // Collect/print the call stack (function, file, line) traces for a single thread. | ||
32 | class CallStack { | ||
32 | public: | 33 | public: |
33 | enum { | 34 | enum { |
34 | MAX_DEPTH = 31 | 35 | // Prune the lowest-most stack frames until we have at most MAX_DEPTH. |
36 | MAX_DEPTH = 31, | ||
37 | // Placeholder for specifying the current thread when updating the stack. | ||
38 | CURRENT_THREAD = -1, | ||
35 | }; | 39 | }; |
36 | 40 | ||
41 | // Create an empty call stack. No-op. | ||
37 | CallStack(); | 42 | CallStack(); |
43 | // Create a callstack with the current thread's stack trace. | ||
44 | // Immediately dump it to logcat using the given logtag. | ||
38 | CallStack(const char* logtag, int32_t ignoreDepth=1, | 45 | CallStack(const char* logtag, int32_t ignoreDepth=1, |
39 | int32_t maxDepth=MAX_DEPTH); | 46 | int32_t maxDepth=MAX_DEPTH); |
47 | // Copy the existing callstack (no other side effects). | ||
40 | CallStack(const CallStack& rhs); | 48 | CallStack(const CallStack& rhs); |
41 | ~CallStack(); | 49 | ~CallStack(); |
42 | 50 | ||
51 | // Copy the existing callstack (no other side effects). | ||
43 | CallStack& operator = (const CallStack& rhs); | 52 | CallStack& operator = (const CallStack& rhs); |
44 | 53 | ||
54 | // Compare call stacks by their backtrace frame memory. | ||
45 | bool operator == (const CallStack& rhs) const; | 55 | bool operator == (const CallStack& rhs) const; |
46 | bool operator != (const CallStack& rhs) const; | 56 | bool operator != (const CallStack& rhs) const; |
47 | bool operator < (const CallStack& rhs) const; | 57 | bool operator < (const CallStack& rhs) const; |
48 | bool operator >= (const CallStack& rhs) const; | 58 | bool operator >= (const CallStack& rhs) const; |
49 | bool operator > (const CallStack& rhs) const; | 59 | bool operator > (const CallStack& rhs) const; |
50 | bool operator <= (const CallStack& rhs) const; | 60 | bool operator <= (const CallStack& rhs) const; |
51 | 61 | ||
62 | // Get the PC address for the stack frame specified by index. | ||
52 | const void* operator [] (int index) const; | 63 | const void* operator [] (int index) const; |
53 | 64 | ||
65 | // Reset the stack frames (same as creating an empty call stack). | ||
54 | void clear(); | 66 | void clear(); |
55 | 67 | ||
56 | void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH); | 68 | // Immediately collect the stack traces for the specified thread. |
69 | void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH, pid_t tid=CURRENT_THREAD); | ||
57 | 70 | ||
58 | // Dump a stack trace to the log using the supplied logtag | 71 | // Dump a stack trace to the log using the supplied logtag. |
59 | void dump(const char* logtag, const char* prefix = 0) const; | 72 | void log(const char* logtag, |
73 | android_LogPriority priority = ANDROID_LOG_DEBUG, | ||
74 | const char* prefix = 0) const; | ||
60 | 75 | ||
61 | // Return a string (possibly very long) containing the complete stack trace | 76 | // Dump a stack trace to the specified file descriptor. |
77 | void dump(int fd, int indent = 0, const char* prefix = 0) const; | ||
78 | |||
79 | // Return a string (possibly very long) containing the complete stack trace. | ||
62 | String8 toString(const char* prefix = 0) const; | 80 | String8 toString(const char* prefix = 0) const; |
63 | 81 | ||
82 | // Dump a serialized representation of the stack trace to the specified printer. | ||
83 | void print(Printer& printer) const; | ||
84 | |||
85 | // Get the count of stack frames that are in this call stack. | ||
64 | size_t size() const { return mCount; } | 86 | size_t size() const { return mCount; } |
65 | 87 | ||
66 | private: | 88 | private: |
@@ -70,7 +92,4 @@ private: | |||
70 | 92 | ||
71 | }; // namespace android | 93 | }; // namespace android |
72 | 94 | ||
73 | |||
74 | // --------------------------------------------------------------------------- | ||
75 | |||
76 | #endif // ANDROID_CALLSTACK_H | 95 | #endif // ANDROID_CALLSTACK_H |
diff --git a/include/utils/Printer.h b/include/utils/Printer.h new file mode 100644 index 000000000..bb6628767 --- /dev/null +++ b/include/utils/Printer.h | |||
@@ -0,0 +1,119 @@ | |||
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 | #ifndef ANDROID_PRINTER_H | ||
18 | #define ANDROID_PRINTER_H | ||
19 | |||
20 | #include <android/log.h> | ||
21 | |||
22 | namespace android { | ||
23 | |||
24 | // Interface for printing to an arbitrary data stream | ||
25 | class Printer { | ||
26 | public: | ||
27 | // Print a new line specified by 'string'. \n is appended automatically. | ||
28 | // -- Assumes that the string has no new line in it. | ||
29 | virtual void printLine(const char* string = "") = 0; | ||
30 | |||
31 | // Print a new line specified by the format string. \n is appended automatically. | ||
32 | // -- Assumes that the resulting string has no new line in it. | ||
33 | virtual void printFormatLine(const char* format, ...) __attribute__((format (printf, 2, 3))); | ||
34 | |||
35 | protected: | ||
36 | Printer(); | ||
37 | virtual ~Printer(); | ||
38 | }; // class Printer | ||
39 | |||
40 | // Print to logcat | ||
41 | class LogPrinter : public Printer { | ||
42 | public: | ||
43 | // Create a printer using the specified logcat and log priority | ||
44 | // - Unless ignoreBlankLines is false, print blank lines to logcat | ||
45 | // (Note that the default ALOG behavior is to ignore blank lines) | ||
46 | LogPrinter(const char* logtag, | ||
47 | android_LogPriority priority = ANDROID_LOG_DEBUG, | ||
48 | const char* prefix = 0, | ||
49 | bool ignoreBlankLines = false); | ||
50 | |||
51 | // Print the specified line to logcat. No \n at the end is necessary. | ||
52 | virtual void printLine(const char* string); | ||
53 | |||
54 | private: | ||
55 | void printRaw(const char* string); | ||
56 | |||
57 | const char* mLogTag; | ||
58 | android_LogPriority mPriority; | ||
59 | const char* mPrefix; | ||
60 | bool mIgnoreBlankLines; | ||
61 | }; // class LogPrinter | ||
62 | |||
63 | // Print to a file descriptor | ||
64 | class FdPrinter : public Printer { | ||
65 | public: | ||
66 | // Create a printer using the specified file descriptor. | ||
67 | // - Each line will be prefixed with 'indent' number of blank spaces. | ||
68 | // - In addition, each line will be prefixed with the 'prefix' string. | ||
69 | FdPrinter(int fd, unsigned int indent = 0, const char* prefix = 0); | ||
70 | |||
71 | // Print the specified line to the file descriptor. \n is appended automatically. | ||
72 | virtual void printLine(const char* string); | ||
73 | |||
74 | private: | ||
75 | enum { | ||
76 | MAX_FORMAT_STRING = 20, | ||
77 | }; | ||
78 | |||
79 | int mFd; | ||
80 | unsigned int mIndent; | ||
81 | const char* mPrefix; | ||
82 | char mFormatString[MAX_FORMAT_STRING]; | ||
83 | }; // class FdPrinter | ||
84 | |||
85 | class String8; | ||
86 | |||
87 | // Print to a String8 | ||
88 | class String8Printer : public Printer { | ||
89 | public: | ||
90 | // Create a printer using the specified String8 as the target. | ||
91 | // - In addition, each line will be prefixed with the 'prefix' string. | ||
92 | // - target's memory lifetime must be a superset of this String8Printer. | ||
93 | String8Printer(String8* target, const char* prefix = 0); | ||
94 | |||
95 | // Append the specified line to the String8. \n is appended automatically. | ||
96 | virtual void printLine(const char* string); | ||
97 | |||
98 | private: | ||
99 | String8* mTarget; | ||
100 | const char* mPrefix; | ||
101 | }; // class String8Printer | ||
102 | |||
103 | // Print to an existing Printer by adding a prefix to each line | ||
104 | class PrefixPrinter : public Printer { | ||
105 | public: | ||
106 | // Create a printer using the specified printer as the target. | ||
107 | PrefixPrinter(Printer& printer, const char* prefix); | ||
108 | |||
109 | // Print the line (prefixed with prefix) using the printer. | ||
110 | virtual void printLine(const char* string); | ||
111 | |||
112 | private: | ||
113 | Printer& mPrinter; | ||
114 | const char* mPrefix; | ||
115 | }; | ||
116 | |||
117 | }; // namespace android | ||
118 | |||
119 | #endif // ANDROID_PRINTER_H | ||
diff --git a/include/utils/ProcessCallStack.h b/include/utils/ProcessCallStack.h new file mode 100644 index 000000000..4a8686958 --- /dev/null +++ b/include/utils/ProcessCallStack.h | |||
@@ -0,0 +1,79 @@ | |||
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 | #ifndef ANDROID_PROCESS_CALLSTACK_H | ||
18 | #define ANDROID_PROCESS_CALLSTACK_H | ||
19 | |||
20 | #include <utils/CallStack.h> | ||
21 | #include <android/log.h> | ||
22 | #include <utils/KeyedVector.h> | ||
23 | #include <utils/String8.h> | ||
24 | |||
25 | #include <time.h> | ||
26 | #include <sys/types.h> | ||
27 | |||
28 | namespace android { | ||
29 | |||
30 | class Printer; | ||
31 | |||
32 | // Collect/print the call stack (function, file, line) traces for all threads in a process. | ||
33 | class ProcessCallStack { | ||
34 | public: | ||
35 | // Create an empty call stack. No-op. | ||
36 | ProcessCallStack(); | ||
37 | // Copy the existing process callstack (no other side effects). | ||
38 | ProcessCallStack(const ProcessCallStack& rhs); | ||
39 | ~ProcessCallStack(); | ||
40 | |||
41 | // Immediately collect the stack traces for all threads. | ||
42 | void update(int32_t maxDepth = CallStack::MAX_DEPTH); | ||
43 | |||
44 | // Print all stack traces to the log using the supplied logtag. | ||
45 | void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG, | ||
46 | const char* prefix = 0) const; | ||
47 | |||
48 | // Dump all stack traces to the specified file descriptor. | ||
49 | void dump(int fd, int indent = 0, const char* prefix = 0) const; | ||
50 | |||
51 | // Return a string (possibly very long) containing all the stack traces. | ||
52 | String8 toString(const char* prefix = 0) const; | ||
53 | |||
54 | // Dump a serialized representation of all the stack traces to the specified printer. | ||
55 | void print(Printer& printer) const; | ||
56 | |||
57 | // Get the number of threads whose stack traces were collected. | ||
58 | size_t size() const; | ||
59 | |||
60 | private: | ||
61 | void printInternal(Printer& printer, Printer& csPrinter) const; | ||
62 | |||
63 | // Reset the process's stack frames and metadata. | ||
64 | void clear(); | ||
65 | |||
66 | struct ThreadInfo { | ||
67 | CallStack callStack; | ||
68 | String8 threadName; | ||
69 | }; | ||
70 | |||
71 | // tid -> ThreadInfo | ||
72 | KeyedVector<pid_t, ThreadInfo> mThreadMap; | ||
73 | // Time that update() was last called | ||
74 | struct tm mTimeUpdated; | ||
75 | }; | ||
76 | |||
77 | }; // namespace android | ||
78 | |||
79 | #endif // ANDROID_PROCESS_CALLSTACK_H | ||
diff --git a/libutils/Android.mk b/libutils/Android.mk index 7e6b1be42..720443e88 100644 --- a/libutils/Android.mk +++ b/libutils/Android.mk | |||
@@ -26,6 +26,8 @@ commonSources:= \ | |||
26 | LinearAllocator.cpp \ | 26 | LinearAllocator.cpp \ |
27 | LinearTransform.cpp \ | 27 | LinearTransform.cpp \ |
28 | Log.cpp \ | 28 | Log.cpp \ |
29 | Printer.cpp \ | ||
30 | ProcessCallStack.cpp \ | ||
29 | PropertyMap.cpp \ | 31 | PropertyMap.cpp \ |
30 | RefBase.cpp \ | 32 | RefBase.cpp \ |
31 | SharedBuffer.cpp \ | 33 | SharedBuffer.cpp \ |
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp index e60f5d8e7..4ceaa7c91 100644 --- a/libutils/CallStack.cpp +++ b/libutils/CallStack.cpp | |||
@@ -16,14 +16,12 @@ | |||
16 | 16 | ||
17 | #define LOG_TAG "CallStack" | 17 | #define LOG_TAG "CallStack" |
18 | 18 | ||
19 | #include <string.h> | ||
20 | |||
21 | #include <utils/Log.h> | ||
22 | #include <utils/Errors.h> | ||
23 | #include <utils/CallStack.h> | 19 | #include <utils/CallStack.h> |
20 | #include <utils/Printer.h> | ||
21 | #include <utils/Errors.h> | ||
22 | #include <utils/Log.h> | ||
24 | #include <corkscrew/backtrace.h> | 23 | #include <corkscrew/backtrace.h> |
25 | 24 | ||
26 | /*****************************************************************************/ | ||
27 | namespace android { | 25 | namespace android { |
28 | 26 | ||
29 | CallStack::CallStack() : | 27 | CallStack::CallStack() : |
@@ -31,8 +29,8 @@ CallStack::CallStack() : | |||
31 | } | 29 | } |
32 | 30 | ||
33 | CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) { | 31 | CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) { |
34 | this->update(ignoreDepth+1, maxDepth); | 32 | this->update(ignoreDepth+1, maxDepth, CURRENT_THREAD); |
35 | this->dump(logtag); | 33 | this->log(logtag); |
36 | } | 34 | } |
37 | 35 | ||
38 | CallStack::CallStack(const CallStack& rhs) : | 36 | CallStack::CallStack(const CallStack& rhs) : |
@@ -93,31 +91,44 @@ void CallStack::clear() { | |||
93 | mCount = 0; | 91 | mCount = 0; |
94 | } | 92 | } |
95 | 93 | ||
96 | void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) { | 94 | void CallStack::update(int32_t ignoreDepth, int32_t maxDepth, pid_t tid) { |
97 | if (maxDepth > MAX_DEPTH) { | 95 | if (maxDepth > MAX_DEPTH) { |
98 | maxDepth = MAX_DEPTH; | 96 | maxDepth = MAX_DEPTH; |
99 | } | 97 | } |
100 | ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth); | 98 | ssize_t count; |
99 | |||
100 | if (tid >= 0) { | ||
101 | count = unwind_backtrace_thread(tid, mStack, ignoreDepth + 1, maxDepth); | ||
102 | } else if (tid == CURRENT_THREAD) { | ||
103 | count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth); | ||
104 | } else { | ||
105 | ALOGE("%s: Invalid tid specified (%d)", __FUNCTION__, tid); | ||
106 | count = 0; | ||
107 | } | ||
108 | |||
101 | mCount = count > 0 ? count : 0; | 109 | mCount = count > 0 ? count : 0; |
102 | } | 110 | } |
103 | 111 | ||
104 | void CallStack::dump(const char* logtag, const char* prefix) const { | 112 | void CallStack::log(const char* logtag, android_LogPriority priority, const char* prefix) const { |
105 | backtrace_symbol_t symbols[mCount]; | 113 | LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false); |
114 | print(printer); | ||
115 | } | ||
106 | 116 | ||
107 | get_backtrace_symbols(mStack, mCount, symbols); | 117 | void CallStack::dump(int fd, int indent, const char* prefix) const { |
108 | for (size_t i = 0; i < mCount; i++) { | 118 | FdPrinter printer(fd, indent, prefix); |
109 | char line[MAX_BACKTRACE_LINE_LENGTH]; | 119 | print(printer); |
110 | format_backtrace_line(i, &mStack[i], &symbols[i], | ||
111 | line, MAX_BACKTRACE_LINE_LENGTH); | ||
112 | ALOG(LOG_DEBUG, logtag, "%s%s", | ||
113 | prefix ? prefix : "", | ||
114 | line); | ||
115 | } | ||
116 | free_backtrace_symbols(symbols, mCount); | ||
117 | } | 120 | } |
118 | 121 | ||
119 | String8 CallStack::toString(const char* prefix) const { | 122 | String8 CallStack::toString(const char* prefix) const { |
120 | String8 str; | 123 | String8 str; |
124 | |||
125 | String8Printer printer(&str, prefix); | ||
126 | print(printer); | ||
127 | |||
128 | return str; | ||
129 | } | ||
130 | |||
131 | void CallStack::print(Printer& printer) const { | ||
121 | backtrace_symbol_t symbols[mCount]; | 132 | backtrace_symbol_t symbols[mCount]; |
122 | 133 | ||
123 | get_backtrace_symbols(mStack, mCount, symbols); | 134 | get_backtrace_symbols(mStack, mCount, symbols); |
@@ -125,14 +136,9 @@ String8 CallStack::toString(const char* prefix) const { | |||
125 | char line[MAX_BACKTRACE_LINE_LENGTH]; | 136 | char line[MAX_BACKTRACE_LINE_LENGTH]; |
126 | format_backtrace_line(i, &mStack[i], &symbols[i], | 137 | format_backtrace_line(i, &mStack[i], &symbols[i], |
127 | line, MAX_BACKTRACE_LINE_LENGTH); | 138 | line, MAX_BACKTRACE_LINE_LENGTH); |
128 | if (prefix) { | 139 | printer.printLine(line); |
129 | str.append(prefix); | ||
130 | } | ||
131 | str.append(line); | ||
132 | str.append("\n"); | ||
133 | } | 140 | } |
134 | free_backtrace_symbols(symbols, mCount); | 141 | free_backtrace_symbols(symbols, mCount); |
135 | return str; | ||
136 | } | 142 | } |
137 | 143 | ||
138 | }; // namespace android | 144 | }; // namespace android |
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp new file mode 100644 index 000000000..b062ef0a7 --- /dev/null +++ b/libutils/Printer.cpp | |||
@@ -0,0 +1,155 @@ | |||
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 "Printer" | ||
18 | // #define LOG_NDEBUG 0 | ||
19 | |||
20 | #include <utils/Printer.h> | ||
21 | #include <utils/String8.h> | ||
22 | #include <utils/Log.h> | ||
23 | |||
24 | #include <string.h> | ||
25 | #include <stdio.h> | ||
26 | #include <stdlib.h> | ||
27 | |||
28 | #ifndef __BIONIC__ | ||
29 | #define fdprintf dprintf | ||
30 | #endif | ||
31 | |||
32 | namespace android { | ||
33 | |||
34 | /* | ||
35 | * Implementation of Printer | ||
36 | */ | ||
37 | Printer::Printer() { | ||
38 | // Intentionally left empty | ||
39 | } | ||
40 | |||
41 | Printer::~Printer() { | ||
42 | // Intentionally left empty | ||
43 | } | ||
44 | |||
45 | void Printer::printFormatLine(const char* format, ...) { | ||
46 | va_list arglist; | ||
47 | va_start(arglist, format); | ||
48 | |||
49 | char* formattedString; | ||
50 | if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error | ||
51 | ALOGE("%s: Failed to format string", __FUNCTION__); | ||
52 | return; | ||
53 | } | ||
54 | va_end(arglist); | ||
55 | |||
56 | printLine(formattedString); | ||
57 | free(formattedString); | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * Implementation of LogPrinter | ||
62 | */ | ||
63 | LogPrinter::LogPrinter(const char* logtag, | ||
64 | android_LogPriority priority, | ||
65 | const char* prefix, | ||
66 | bool ignoreBlankLines) : | ||
67 | mLogTag(logtag), | ||
68 | mPriority(priority), | ||
69 | mPrefix(prefix ?: ""), | ||
70 | mIgnoreBlankLines(ignoreBlankLines) { | ||
71 | } | ||
72 | |||
73 | void LogPrinter::printLine(const char* string) { | ||
74 | if (string == NULL) { | ||
75 | ALOGW("%s: NULL string passed in", __FUNCTION__); | ||
76 | return; | ||
77 | } | ||
78 | |||
79 | if (mIgnoreBlankLines || (*string)) { | ||
80 | // Simple case: Line is not blank, or we don't care about printing blank lines | ||
81 | printRaw(string); | ||
82 | } else { | ||
83 | // Force logcat to print empty lines by adding prefixing with a space | ||
84 | printRaw(" "); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | void LogPrinter::printRaw(const char* string) { | ||
89 | __android_log_print(mPriority, mLogTag, "%s%s", mPrefix, string); | ||
90 | } | ||
91 | |||
92 | |||
93 | /* | ||
94 | * Implementation of FdPrinter | ||
95 | */ | ||
96 | FdPrinter::FdPrinter(int fd, unsigned int indent, const char* prefix) : | ||
97 | mFd(fd), mIndent(indent), mPrefix(prefix ?: "") { | ||
98 | |||
99 | if (fd < 0) { | ||
100 | ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, fd); | ||
101 | } | ||
102 | |||
103 | // <indent><prefix><line> -- e.g. '%-4s%s\n' for indent=4 | ||
104 | snprintf(mFormatString, sizeof(mFormatString), "%%-%us%%s\n", mIndent); | ||
105 | } | ||
106 | |||
107 | void FdPrinter::printLine(const char* string) { | ||
108 | if (string == NULL) { | ||
109 | ALOGW("%s: NULL string passed in", __FUNCTION__); | ||
110 | return; | ||
111 | } else if (mFd < 0) { | ||
112 | ALOGW("%s: File descriptor out of range (%d)", __FUNCTION__, mFd); | ||
113 | return; | ||
114 | } | ||
115 | |||
116 | fdprintf(mFd, mFormatString, mPrefix, string); | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Implementation of String8Printer | ||
121 | */ | ||
122 | String8Printer::String8Printer(String8* target, const char* prefix) : | ||
123 | mTarget(target), | ||
124 | mPrefix(prefix ?: "") { | ||
125 | |||
126 | if (target == NULL) { | ||
127 | ALOGW("%s: Target string was NULL", __FUNCTION__); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | void String8Printer::printLine(const char* string) { | ||
132 | if (string == NULL) { | ||
133 | ALOGW("%s: NULL string passed in", __FUNCTION__); | ||
134 | return; | ||
135 | } else if (mTarget == NULL) { | ||
136 | ALOGW("%s: Target string was NULL", __FUNCTION__); | ||
137 | return; | ||
138 | } | ||
139 | |||
140 | mTarget->append(string); | ||
141 | mTarget->append("\n"); | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * Implementation of PrefixPrinter | ||
146 | */ | ||
147 | PrefixPrinter::PrefixPrinter(Printer& printer, const char* prefix) : | ||
148 | mPrinter(printer), mPrefix(prefix ?: "") { | ||
149 | } | ||
150 | |||
151 | void PrefixPrinter::printLine(const char* string) { | ||
152 | mPrinter.printFormatLine("%s%s", mPrefix, string); | ||
153 | } | ||
154 | |||
155 | }; //namespace android | ||
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp new file mode 100644 index 000000000..ed35237a0 --- /dev/null +++ b/libutils/ProcessCallStack.cpp | |||
@@ -0,0 +1,256 @@ | |||
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 | 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 | // Strip ending newline | ||
92 | strtok(procName, "\n"); | ||
93 | |||
94 | return String8(procName); | ||
95 | } | ||
96 | |||
97 | static 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 | */ | ||
108 | ProcessCallStack::ProcessCallStack() { | ||
109 | } | ||
110 | |||
111 | ProcessCallStack::ProcessCallStack(const ProcessCallStack& rhs) : | ||
112 | mThreadMap(rhs.mThreadMap), | ||
113 | mTimeUpdated(rhs.mTimeUpdated) { | ||
114 | } | ||
115 | |||
116 | ProcessCallStack::~ProcessCallStack() { | ||
117 | } | ||
118 | |||
119 | void ProcessCallStack::clear() { | ||
120 | mThreadMap.clear(); | ||
121 | mTimeUpdated = tm(); | ||
122 | } | ||
123 | |||
124 | void 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 | |||
198 | void 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 | |||
204 | void 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 | |||
213 | void 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 | |||
232 | void 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 | |||
243 | String8 ProcessCallStack::toString(const char* prefix) const { | ||
244 | |||
245 | String8 dest; | ||
246 | String8Printer printer(&dest, prefix); | ||
247 | print(printer); | ||
248 | |||
249 | return dest; | ||
250 | } | ||
251 | |||
252 | size_t ProcessCallStack::size() const { | ||
253 | return mThreadMap.size(); | ||
254 | } | ||
255 | |||
256 | }; //namespace android | ||