OLD | NEW |
1 // Copyright (c) 2009, Google Inc. | 1 // Copyright (c) 2009, Google Inc. |
2 // All rights reserved. | 2 // All rights reserved. |
3 // | 3 // |
4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
6 // met: | 6 // met: |
7 // | 7 // |
8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 #endif | 50 #endif |
51 #ifdef __MACH__ | 51 #ifdef __MACH__ |
52 #include <mach-o/dyld.h> // for GetProgramInvocationName() | 52 #include <mach-o/dyld.h> // for GetProgramInvocationName() |
53 #include <limits.h> // for PATH_MAX | 53 #include <limits.h> // for PATH_MAX |
54 #endif | 54 #endif |
55 #if defined(__CYGWIN__) || defined(__CYGWIN32__) | 55 #if defined(__CYGWIN__) || defined(__CYGWIN32__) |
56 #include <io.h> // for get_osfhandle() | 56 #include <io.h> // for get_osfhandle() |
57 #endif | 57 #endif |
58 #include <string> | 58 #include <string> |
59 #include "base/commandlineflags.h" | 59 #include "base/commandlineflags.h" |
60 #include "base/logging.h" | |
61 #include "base/sysinfo.h" | 60 #include "base/sysinfo.h" |
62 | 61 |
63 using std::string; | 62 using std::string; |
64 using tcmalloc::DumpProcSelfMaps; // from sysinfo.h | 63 using tcmalloc::DumpProcSelfMaps; // from sysinfo.h |
65 | 64 |
66 | 65 |
67 DEFINE_string(symbolize_pprof, | 66 DEFINE_string(symbolize_pprof, |
68 EnvToString("PPROF_PATH", "pprof"), | 67 EnvToString("PPROF_PATH", "pprof"), |
69 "Path to pprof to call for reporting function names."); | 68 "Path to pprof to call for reporting function names."); |
70 | 69 |
(...skipping 16 matching lines...) Expand all Loading... |
87 uint32_t length = sizeof(program_invocation_name); | 86 uint32_t length = sizeof(program_invocation_name); |
88 if (_NSGetExecutablePath(program_invocation_name, &length)) | 87 if (_NSGetExecutablePath(program_invocation_name, &length)) |
89 return NULL; | 88 return NULL; |
90 } | 89 } |
91 return program_invocation_name; | 90 return program_invocation_name; |
92 #else | 91 #else |
93 return NULL; // figure out a way to get argv[0] | 92 return NULL; // figure out a way to get argv[0] |
94 #endif | 93 #endif |
95 } | 94 } |
96 | 95 |
97 // Prints an error message when you can't run Symbolize(). | |
98 static void PrintError(const char* reason) { | |
99 RAW_LOG(ERROR, | |
100 "*** WARNING: Cannot convert addresses to symbols in output below.\n" | |
101 "*** Reason: %s\n" | |
102 "*** If you cannot fix this, try running pprof directly.\n", | |
103 reason); | |
104 } | |
105 | |
106 void SymbolTable::Add(const void* addr) { | 96 void SymbolTable::Add(const void* addr) { |
107 symbolization_table_[addr] = ""; | 97 symbolization_table_[addr] = ""; |
108 } | 98 } |
109 | 99 |
110 const char* SymbolTable::GetSymbol(const void* addr) { | 100 const char* SymbolTable::GetSymbol(const void* addr) { |
111 return symbolization_table_[addr]; | 101 return symbolization_table_[addr]; |
112 } | 102 } |
113 | 103 |
114 // Updates symbolization_table with the pointers to symbol names corresponding | 104 // Updates symbolization_table with the pointers to symbol names corresponding |
115 // to its keys. The symbol names are stored in out, which is allocated and | 105 // to its keys. The symbol names are stored in out, which is allocated and |
116 // freed by the caller of this routine. | 106 // freed by the caller of this routine. |
117 // Note that the forking/etc is not thread-safe or re-entrant. That's | 107 // Note that the forking/etc is not thread-safe or re-entrant. That's |
118 // ok for the purpose we need -- reporting leaks detected by heap-checker | 108 // ok for the purpose we need -- reporting leaks detected by heap-checker |
119 // -- but be careful if you decide to use this routine for other purposes. | 109 // -- but be careful if you decide to use this routine for other purposes. |
120 // Returns number of symbols read on error. If can't symbolize, returns 0 | |
121 // and emits an error message about why. | |
122 int SymbolTable::Symbolize() { | 110 int SymbolTable::Symbolize() { |
123 #if !defined(HAVE_UNISTD_H) || !defined(HAVE_SYS_SOCKET_H) || !defined(HAVE_SYS
_WAIT_H) | 111 #if !defined(HAVE_UNISTD_H) || !defined(HAVE_SYS_SOCKET_H) || !defined(HAVE_SYS
_WAIT_H) |
124 PrintError("Perftools does not know how to call a sub-process on this O/S"); | |
125 return 0; | 112 return 0; |
126 #else | 113 #else |
127 const char* argv0 = GetProgramInvocationName(); | 114 const char* argv0 = GetProgramInvocationName(); |
128 if (argv0 == NULL) { // can't call symbolize if we can't figure out our name | 115 if (argv0 == NULL) // can't call symbolize if we can't figure out our name |
129 PrintError("Cannot figure out the name of this executable (argv0)"); | |
130 return 0; | 116 return 0; |
131 } | |
132 if (access(g_pprof_path->c_str(), R_OK) != 0) { | |
133 PrintError("Cannot find 'pprof' (is PPROF_PATH set correctly?)"); | |
134 return 0; | |
135 } | |
136 | 117 |
137 // All this work is to do two-way communication. ugh. | 118 // All this work is to do two-way communication. ugh. |
138 int *child_in = NULL; // file descriptors | 119 int *child_in = NULL; // file descriptors |
139 int *child_out = NULL; // for now, we don't worry about child_err | 120 int *child_out = NULL; // for now, we don't worry about child_err |
140 int child_fds[5][2]; // socketpair may be called up to five times below | 121 int child_fds[5][2]; // socketpair may be called up to five times below |
141 | 122 |
142 // The client program may close its stdin and/or stdout and/or stderr | 123 // The client program may close its stdin and/or stdout and/or stderr |
143 // thus allowing socketpair to reuse file descriptors 0, 1 or 2. | 124 // thus allowing socketpair to reuse file descriptors 0, 1 or 2. |
144 // In this case the communication between the forked processes may be broken | 125 // In this case the communication between the forked processes may be broken |
145 // if either the parent or the child tries to close or duplicate these | 126 // if either the parent or the child tries to close or duplicate these |
146 // descriptors. The loop below produces two pairs of file descriptors, each | 127 // descriptors. The loop below produces two pairs of file descriptors, each |
147 // greater than 2 (stderr). | 128 // greater than 2 (stderr). |
148 for (int i = 0; i < 5; i++) { | 129 for (int i = 0; i < 5; i++) { |
149 if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_fds[i]) == -1) { | 130 if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_fds[i]) == -1) { |
150 for (int j = 0; j < i; j++) { | 131 for (int j = 0; j < i; j++) { |
151 close(child_fds[j][0]); | 132 close(child_fds[j][0]); |
152 close(child_fds[j][1]); | 133 close(child_fds[j][1]); |
153 PrintError("Cannot create a socket pair"); | |
154 return 0; | 134 return 0; |
155 } | 135 } |
156 } else { | 136 } else { |
157 if ((child_fds[i][0] > 2) && (child_fds[i][1] > 2)) { | 137 if ((child_fds[i][0] > 2) && (child_fds[i][1] > 2)) { |
158 if (child_in == NULL) { | 138 if (child_in == NULL) { |
159 child_in = child_fds[i]; | 139 child_in = child_fds[i]; |
160 } else { | 140 } else { |
161 child_out = child_fds[i]; | 141 child_out = child_fds[i]; |
162 for (int j = 0; j < i; j++) { | 142 for (int j = 0; j < i; j++) { |
163 if (child_fds[j] == child_in) continue; | 143 if (child_fds[j] == child_in) continue; |
164 close(child_fds[j][0]); | 144 close(child_fds[j][0]); |
165 close(child_fds[j][1]); | 145 close(child_fds[j][1]); |
166 } | 146 } |
167 break; | 147 break; |
168 } | 148 } |
169 } | 149 } |
170 } | 150 } |
171 } | 151 } |
172 | 152 |
173 switch (fork()) { | 153 switch (fork()) { |
174 case -1: { // error | 154 case -1: { // error |
175 close(child_in[0]); | 155 close(child_in[0]); |
176 close(child_in[1]); | 156 close(child_in[1]); |
177 close(child_out[0]); | 157 close(child_out[0]); |
178 close(child_out[1]); | 158 close(child_out[1]); |
179 PrintError("Unknown error calling fork()"); | |
180 return 0; | 159 return 0; |
181 } | 160 } |
182 case 0: { // child | 161 case 0: { // child |
183 close(child_in[1]); // child uses the 0's, parent uses the 1's | 162 close(child_in[1]); // child uses the 0's, parent uses the 1's |
184 close(child_out[1]); // child uses the 0's, parent uses the 1's | 163 close(child_out[1]); // child uses the 0's, parent uses the 1's |
185 close(0); | 164 close(0); |
186 close(1); | 165 close(1); |
187 if (dup2(child_in[0], 0) == -1) _exit(1); | 166 if (dup2(child_in[0], 0) == -1) _exit(1); |
188 if (dup2(child_out[0], 1) == -1) _exit(2); | 167 if (dup2(child_out[0], 1) == -1) _exit(2); |
189 // Unset vars that might cause trouble when we fork | 168 // Unset vars that might cause trouble when we fork |
190 unsetenv("CPUPROFILE"); | 169 unsetenv("CPUPROFILE"); |
191 unsetenv("HEAPPROFILE"); | 170 unsetenv("HEAPPROFILE"); |
192 unsetenv("HEAPCHECK"); | 171 unsetenv("HEAPCHECK"); |
193 unsetenv("PERFTOOLS_VERBOSE"); | 172 unsetenv("PERFTOOLS_VERBOSE"); |
194 execlp(g_pprof_path->c_str(), g_pprof_path->c_str(), | 173 execlp(g_pprof_path->c_str(), g_pprof_path->c_str(), |
195 "--symbols", argv0, NULL); | 174 "--symbols", argv0, NULL); |
196 _exit(3); // if execvp fails, it's bad news for us | 175 _exit(3); // if execvp fails, it's bad news for us |
197 } | 176 } |
198 default: { // parent | 177 default: { // parent |
199 close(child_in[0]); // child uses the 0's, parent uses the 1's | 178 close(child_in[0]); // child uses the 0's, parent uses the 1's |
200 close(child_out[0]); // child uses the 0's, parent uses the 1's | 179 close(child_out[0]); // child uses the 0's, parent uses the 1's |
201 #ifdef HAVE_POLL_H | 180 #ifdef HAVE_POLL_H |
202 // Waiting for 1ms seems to give the OS time to notice any errors. | |
203 poll(0, 0, 1); | |
204 // For maximum safety, we check to make sure the execlp | 181 // For maximum safety, we check to make sure the execlp |
205 // succeeded before trying to write. (Otherwise we'll get a | 182 // succeeded before trying to write. (Otherwise we'll get a |
206 // SIGPIPE.) For systems without poll.h, we'll just skip this | 183 // SIGPIPE.) For systems without poll.h, we'll just skip this |
207 // check, and trust that the user set PPROF_PATH correctly! | 184 // check, and trust that the user set PPROF_PATH correctly! |
208 struct pollfd pfd = { child_in[1], POLLOUT, 0 }; | 185 struct pollfd pfd = { child_in[1], POLLOUT, 0 }; |
209 if (!poll(&pfd, 1, 0) || !(pfd.revents & POLLOUT) || | 186 if (!poll(&pfd, 1, 0) || !(pfd.revents & POLLOUT) || |
210 (pfd.revents & (POLLHUP|POLLERR))) { | 187 (pfd.revents & (POLLHUP|POLLERR))) { |
211 PrintError("Cannot run 'pprof' (is PPROF_PATH set correctly?)"); | |
212 return 0; | 188 return 0; |
213 } | 189 } |
214 #endif | 190 #endif |
215 #if defined(__CYGWIN__) || defined(__CYGWIN32__) | 191 #if defined(__CYGWIN__) || defined(__CYGWIN32__) |
216 // On cygwin, DumpProcSelfMaps() takes a HANDLE, not an fd. Convert. | 192 // On cygwin, DumpProcSelfMaps() takes a HANDLE, not an fd. Convert. |
217 const HANDLE symbols_handle = (HANDLE) get_osfhandle(child_in[1]); | 193 const HANDLE symbols_handle = (HANDLE) get_osfhandle(child_in[1]); |
218 DumpProcSelfMaps(symbols_handle); | 194 DumpProcSelfMaps(symbols_handle); |
219 #else | 195 #else |
220 DumpProcSelfMaps(child_in[1]); // what pprof expects on stdin | 196 DumpProcSelfMaps(child_in[1]); // what pprof expects on stdin |
221 #endif | 197 #endif |
(...skipping 15 matching lines...) Expand all Loading... |
237 const int kSymbolBufferSize = kSymbolSize * symbolization_table_.size(); | 213 const int kSymbolBufferSize = kSymbolSize * symbolization_table_.size(); |
238 int total_bytes_read = 0; | 214 int total_bytes_read = 0; |
239 delete[] symbol_buffer_; | 215 delete[] symbol_buffer_; |
240 symbol_buffer_ = new char[kSymbolBufferSize]; | 216 symbol_buffer_ = new char[kSymbolBufferSize]; |
241 memset(symbol_buffer_, '\0', kSymbolBufferSize); | 217 memset(symbol_buffer_, '\0', kSymbolBufferSize); |
242 while (1) { | 218 while (1) { |
243 int bytes_read = read(child_out[1], symbol_buffer_ + total_bytes_read, | 219 int bytes_read = read(child_out[1], symbol_buffer_ + total_bytes_read, |
244 kSymbolBufferSize - total_bytes_read); | 220 kSymbolBufferSize - total_bytes_read); |
245 if (bytes_read < 0) { | 221 if (bytes_read < 0) { |
246 close(child_out[1]); | 222 close(child_out[1]); |
247 PrintError("Cannot read data from pprof"); | |
248 return 0; | 223 return 0; |
249 } else if (bytes_read == 0) { | 224 } else if (bytes_read == 0) { |
250 close(child_out[1]); | 225 close(child_out[1]); |
251 wait(NULL); | 226 wait(NULL); |
252 break; | 227 break; |
253 } else { | 228 } else { |
254 total_bytes_read += bytes_read; | 229 total_bytes_read += bytes_read; |
255 } | 230 } |
256 } | 231 } |
257 // We have successfully read the output of pprof into out. Make sure | 232 // We have successfully read the output of pprof into out. Make sure |
258 // the last symbol is full (we can tell because it ends with a \n). | 233 // the last symbol is full (we can tell because it ends with a \n). |
259 if (total_bytes_read == 0 || symbol_buffer_[total_bytes_read - 1] != '\n') | 234 if (total_bytes_read == 0 || symbol_buffer_[total_bytes_read - 1] != '\n') |
260 return 0; | 235 return 0; |
261 // make the symbolization_table_ values point to the output vector | 236 // make the symbolization_table_ values point to the output vector |
262 SymbolMap::iterator fill = symbolization_table_.begin(); | 237 SymbolMap::iterator fill = symbolization_table_.begin(); |
263 int num_symbols = 0; | 238 int num_symbols = 0; |
264 const char *current_name = symbol_buffer_; | 239 const char *current_name = symbol_buffer_; |
265 for (int i = 0; i < total_bytes_read; i++) { | 240 for (int i = 0; i < total_bytes_read; i++) { |
266 if (symbol_buffer_[i] == '\n') { | 241 if (symbol_buffer_[i] == '\n') { |
267 fill->second = current_name; | 242 fill->second = current_name; |
268 symbol_buffer_[i] = '\0'; | 243 symbol_buffer_[i] = '\0'; |
269 current_name = symbol_buffer_ + i + 1; | 244 current_name = symbol_buffer_ + i + 1; |
270 fill++; | 245 fill++; |
271 num_symbols++; | 246 num_symbols++; |
272 } | 247 } |
273 } | 248 } |
274 return num_symbols; | 249 return num_symbols; |
275 } | 250 } |
276 } | 251 } |
277 PrintError("Unkown error (should never occur!)"); | |
278 return 0; // shouldn't be reachable | 252 return 0; // shouldn't be reachable |
279 #endif | 253 #endif |
280 } | 254 } |
OLD | NEW |