OLD | NEW |
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "crash-reporter/user_collector.h" | 5 #include "crash-reporter/user_collector.h" |
6 | 6 |
7 #include <fcntl.h> // For creat. | 7 #include <fcntl.h> // For creat. |
8 #include <grp.h> // For struct group. | 8 #include <grp.h> // For struct group. |
9 #include <pwd.h> // For struct passwd. | 9 #include <pwd.h> // For struct passwd. |
10 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. | 10 #include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS. |
11 #include <sys/wait.h> // For waitpid. | 11 #include <sys/wait.h> // For waitpid. |
12 #include <unistd.h> // For execv and fork. | 12 #include <unistd.h> // For execv and fork. |
13 | 13 |
14 #include <string> | 14 #include <string> |
15 #include <vector> | 15 #include <vector> |
16 | 16 |
17 #include "base/eintr_wrapper.h" | 17 #include "base/eintr_wrapper.h" |
18 #include "base/file_util.h" | 18 #include "base/file_util.h" |
19 #include "base/logging.h" | 19 #include "base/logging.h" |
20 #include "base/string_util.h" | 20 #include "base/string_util.h" |
21 #include "crash-reporter/system_logging.h" | 21 #include "crash-reporter/system_logging.h" |
| 22 #include "gflags/gflags.h" |
22 | 23 |
| 24 DEFINE_bool(core2md_failure_test, false, "Core2md failure test"); |
| 25 DEFINE_bool(directory_failure_test, false, "Spool directory failure test"); |
| 26 |
| 27 static const char kCollectionErrorSignature[] = |
| 28 "crash_reporter-user-collection"; |
23 // This procfs file is used to cause kernel core file writing to | 29 // This procfs file is used to cause kernel core file writing to |
24 // instead pipe the core file into a user space process. See | 30 // instead pipe the core file into a user space process. See |
25 // core(5) man page. | 31 // core(5) man page. |
26 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; | 32 static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern"; |
27 static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; | 33 static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md"; |
28 static const char kLeaveCoreFile[] = "/root/.leave_core"; | 34 static const char kLeaveCoreFile[] = "/root/.leave_core"; |
29 | 35 |
30 const char *UserCollector::kUserId = "Uid:\t"; | 36 const char *UserCollector::kUserId = "Uid:\t"; |
31 const char *UserCollector::kGroupId = "Gid:\t"; | 37 const char *UserCollector::kGroupId = "Gid:\t"; |
32 | 38 |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
142 return false; | 148 return false; |
143 } | 149 } |
144 const char *number = ids[kind].c_str(); | 150 const char *number = ids[kind].c_str(); |
145 char *end_number = NULL; | 151 char *end_number = NULL; |
146 *id = strtol(number, &end_number, 10); | 152 *id = strtol(number, &end_number, 10); |
147 if (*end_number != '\0') | 153 if (*end_number != '\0') |
148 return false; | 154 return false; |
149 return true; | 155 return true; |
150 } | 156 } |
151 | 157 |
| 158 void UserCollector::LogCollectionError(const std::string &error_message) { |
| 159 error_log_.append(error_message.c_str()); |
| 160 error_log_.append("\n"); |
| 161 logger_->LogError(error_message.c_str()); |
| 162 } |
| 163 |
| 164 void UserCollector::EnqueueCollectionErrorLog(pid_t pid, |
| 165 const std::string &exec) { |
| 166 FilePath crash_path; |
| 167 logger_->LogInfo("Writing conversion problems as separate crash report."); |
| 168 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) { |
| 169 logger_->LogError("Could not even get log directory; out of space?"); |
| 170 return; |
| 171 } |
| 172 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); |
| 173 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log"); |
| 174 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); |
| 175 file_util::WriteFile(log_path, |
| 176 error_log_.data(), |
| 177 error_log_.length()); |
| 178 AddCrashMetaData("sig", kCollectionErrorSignature); |
| 179 WriteCrashMetaData(meta_path, exec, log_path.value()); |
| 180 } |
| 181 |
152 bool UserCollector::CopyOffProcFiles(pid_t pid, | 182 bool UserCollector::CopyOffProcFiles(pid_t pid, |
153 const FilePath &container_dir) { | 183 const FilePath &container_dir) { |
154 if (!file_util::CreateDirectory(container_dir)) { | 184 if (!file_util::CreateDirectory(container_dir)) { |
155 logger_->LogInfo("Could not create %s", container_dir.value().c_str()); | 185 LogCollectionError(StringPrintf("Could not create %s", |
| 186 container_dir.value().c_str())); |
156 return false; | 187 return false; |
157 } | 188 } |
158 FilePath process_path = GetProcessPath(pid); | 189 FilePath process_path = GetProcessPath(pid); |
159 if (!file_util::PathExists(process_path)) { | 190 if (!file_util::PathExists(process_path)) { |
160 logger_->LogWarning("Path %s does not exist", | 191 LogCollectionError(StringPrintf("Path %s does not exist", |
161 process_path.value().c_str()); | 192 process_path.value().c_str())); |
162 return false; | 193 return false; |
163 } | 194 } |
164 static const char *proc_files[] = { | 195 static const char *proc_files[] = { |
165 "auxv", | 196 "auxv", |
166 "cmdline", | 197 "cmdline", |
167 "environ", | 198 "environ", |
168 "maps", | 199 "maps", |
169 "status" | 200 "status" |
170 }; | 201 }; |
171 for (unsigned i = 0; i < arraysize(proc_files); ++i) { | 202 for (unsigned i = 0; i < arraysize(proc_files); ++i) { |
172 if (!file_util::CopyFile(process_path.Append(proc_files[i]), | 203 if (!file_util::CopyFile(process_path.Append(proc_files[i]), |
173 container_dir.Append(proc_files[i]))) { | 204 container_dir.Append(proc_files[i]))) { |
174 logger_->LogWarning("Could not copy %s file", proc_files[i]); | 205 LogCollectionError(StringPrintf("Could not copy %s file", |
| 206 proc_files[i])); |
175 return false; | 207 return false; |
176 } | 208 } |
177 } | 209 } |
178 return true; | 210 return true; |
179 } | 211 } |
180 | 212 |
181 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, | 213 bool UserCollector::GetCreatedCrashDirectory(pid_t pid, |
182 FilePath *crash_file_path) { | 214 FilePath *crash_file_path, |
| 215 bool *out_of_capacity) { |
183 FilePath process_path = GetProcessPath(pid); | 216 FilePath process_path = GetProcessPath(pid); |
184 std::string status; | 217 std::string status; |
| 218 if (FLAGS_directory_failure_test) { |
| 219 LogCollectionError("Purposefully failing to create spool directory"); |
| 220 return false; |
| 221 } |
185 if (!file_util::ReadFileToString(process_path.Append("status"), | 222 if (!file_util::ReadFileToString(process_path.Append("status"), |
186 &status)) { | 223 &status)) { |
187 logger_->LogError("Could not read status file"); | 224 LogCollectionError("Could not read status file"); |
188 return false; | 225 return false; |
189 } | 226 } |
190 int process_euid; | 227 int process_euid; |
191 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { | 228 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) { |
192 logger_->LogError("Could not find euid in status file"); | 229 LogCollectionError("Could not find euid in status file"); |
193 return false; | 230 return false; |
194 } | 231 } |
195 return GetCreatedCrashDirectoryByEuid(process_euid, | 232 if (!GetCreatedCrashDirectoryByEuid(process_euid, |
196 crash_file_path); | 233 crash_file_path, |
| 234 out_of_capacity)) { |
| 235 LogCollectionError("Could not create crash directory"); |
| 236 return false; |
| 237 } |
| 238 return true; |
197 } | 239 } |
198 | 240 |
199 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { | 241 bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) { |
200 // Copy off all stdin to a core file. | 242 // Copy off all stdin to a core file. |
201 FilePath stdin_path("/dev/fd/0"); | 243 FilePath stdin_path("/dev/fd/0"); |
202 if (file_util::CopyFile(stdin_path, core_path)) { | 244 if (file_util::CopyFile(stdin_path, core_path)) { |
203 return true; | 245 return true; |
204 } | 246 } |
205 | 247 |
206 logger_->LogError("Could not write core file"); | 248 LogCollectionError("Could not write core file"); |
207 // If the file system was full, make sure we remove any remnants. | 249 // If the file system was full, make sure we remove any remnants. |
208 file_util::Delete(core_path, false); | 250 file_util::Delete(core_path, false); |
209 return false; | 251 return false; |
210 } | 252 } |
211 | 253 |
212 int UserCollector::ForkExecAndPipe(std::vector<const char *> &arguments, | 254 int UserCollector::ForkExecAndPipe(std::vector<const char *> &arguments, |
213 const char *output_file) { | 255 const char *output_file) { |
214 // Copy off a writeable version of arguments. | 256 // Copy off a writeable version of arguments. |
215 scoped_array<char*> argv(new char *[arguments.size() + 1]); | 257 scoped_array<char*> argv(new char *[arguments.size() + 1]); |
216 int total_args_size = 0; | 258 int total_args_size = 0; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
252 logger_->LogError("Exec failed: %d", errno); | 294 logger_->LogError("Exec failed: %d", errno); |
253 _exit(127); | 295 _exit(127); |
254 } | 296 } |
255 | 297 |
256 int status = 0; | 298 int status = 0; |
257 if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) { | 299 if (HANDLE_EINTR(waitpid(pid, &status, 0)) < 0) { |
258 logger_->LogError("Problem waiting for pid: %d", errno); | 300 logger_->LogError("Problem waiting for pid: %d", errno); |
259 return -1; | 301 return -1; |
260 } | 302 } |
261 if (!WIFEXITED(status)) { | 303 if (!WIFEXITED(status)) { |
262 logger_->LogError("Process did not exit normally: %x", status); | 304 logger_->LogError("Process did not exit normally: %d", status); |
263 return -1; | 305 return -1; |
264 } | 306 } |
265 return WEXITSTATUS(status); | 307 return WEXITSTATUS(status); |
266 } | 308 } |
267 | 309 |
268 bool UserCollector::ConvertCoreToMinidump(const FilePath &core_path, | 310 bool UserCollector::RunCoreToMinidump(const FilePath &core_path, |
269 const FilePath &procfs_directory, | 311 const FilePath &procfs_directory, |
270 const FilePath &minidump_path, | 312 const FilePath &minidump_path, |
271 const FilePath &temp_directory) { | 313 const FilePath &temp_directory) { |
272 FilePath output_path = temp_directory.Append("output"); | 314 FilePath output_path = temp_directory.Append("output"); |
273 std::vector<const char *> core2md_arguments; | 315 std::vector<const char *> core2md_arguments; |
274 core2md_arguments.push_back(kCoreToMinidumpConverterPath); | 316 core2md_arguments.push_back(kCoreToMinidumpConverterPath); |
275 core2md_arguments.push_back(core_path.value().c_str()); | 317 core2md_arguments.push_back(core_path.value().c_str()); |
276 core2md_arguments.push_back(procfs_directory.value().c_str()); | 318 core2md_arguments.push_back(procfs_directory.value().c_str()); |
277 core2md_arguments.push_back(minidump_path.value().c_str()); | 319 core2md_arguments.push_back(minidump_path.value().c_str()); |
278 | 320 |
| 321 if (FLAGS_core2md_failure_test) { |
| 322 // To test how core2md errors are propagaged, cause an error |
| 323 // by forgetting a required argument. |
| 324 core2md_arguments.pop_back(); |
| 325 } |
| 326 |
279 int errorlevel = ForkExecAndPipe(core2md_arguments, | 327 int errorlevel = ForkExecAndPipe(core2md_arguments, |
280 output_path.value().c_str()); | 328 output_path.value().c_str()); |
281 | 329 |
282 std::string output; | 330 std::string output; |
283 file_util::ReadFileToString(output_path, &output); | 331 file_util::ReadFileToString(output_path, &output); |
284 if (errorlevel != 0) { | 332 if (errorlevel != 0) { |
285 logger_->LogInfo("Problem during %s [result=%d]: %s", | 333 LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s", |
286 kCoreToMinidumpConverterPath, | 334 kCoreToMinidumpConverterPath, |
287 errorlevel, | 335 errorlevel, |
288 output.c_str()); | 336 output.c_str())); |
289 return false; | 337 return false; |
290 } | 338 } |
291 | 339 |
292 if (!file_util::PathExists(minidump_path)) { | 340 if (!file_util::PathExists(minidump_path)) { |
293 logger_->LogError("Minidump file %s was not created", | 341 LogCollectionError(StringPrintf("Minidump file %s was not created", |
294 minidump_path.value().c_str()); | 342 minidump_path.value().c_str())); |
295 return false; | 343 return false; |
296 } | 344 } |
297 return true; | 345 return true; |
298 } | 346 } |
299 | 347 |
300 bool UserCollector::GenerateDiagnostics(pid_t pid, | 348 bool UserCollector::ConvertCoreToMinidump(pid_t pid, |
301 const std::string &exec_name) { | 349 const FilePath &container_dir, |
302 FilePath container_dir("/tmp"); | 350 const FilePath &core_path, |
303 container_dir = container_dir.Append( | 351 const FilePath &minidump_path) { |
304 StringPrintf("crash_reporter.%d", pid)); | |
305 | |
306 if (!CopyOffProcFiles(pid, container_dir)) { | 352 if (!CopyOffProcFiles(pid, container_dir)) { |
307 file_util::Delete(container_dir, true); | |
308 return false; | 353 return false; |
309 } | 354 } |
310 | 355 |
311 FilePath crash_path; | |
312 if (!GetCreatedCrashDirectory(pid, &crash_path)) { | |
313 file_util::Delete(container_dir, true); | |
314 return false; | |
315 } | |
316 std::string dump_basename = FormatDumpBasename(exec_name, time(NULL), pid); | |
317 FilePath core_path = crash_path.Append( | |
318 StringPrintf("%s.core", dump_basename.c_str())); | |
319 | |
320 if (!CopyStdinToCoreFile(core_path)) { | 356 if (!CopyStdinToCoreFile(core_path)) { |
321 file_util::Delete(container_dir, true); | |
322 return false; | 357 return false; |
323 } | 358 } |
324 | 359 |
325 FilePath minidump_path = crash_path.Append( | 360 bool conversion_result = RunCoreToMinidump( |
326 StringPrintf("%s.dmp", dump_basename.c_str())); | 361 core_path, |
327 | 362 container_dir, // procfs directory |
328 bool conversion_result = true; | 363 minidump_path, |
329 if (!ConvertCoreToMinidump(core_path, | 364 container_dir); // temporary directory |
330 container_dir, // procfs directory | |
331 minidump_path, | |
332 container_dir)) { // temporary directory | |
333 // Note we leave the container directory for inspection. | |
334 conversion_result = false; | |
335 } else { | |
336 file_util::Delete(container_dir, true); | |
337 } | |
338 | |
339 WriteCrashMetaData( | |
340 crash_path.Append( | |
341 StringPrintf("%s.meta", dump_basename.c_str())), | |
342 exec_name, | |
343 minidump_path.value()); | |
344 | 365 |
345 if (conversion_result) { | 366 if (conversion_result) { |
346 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); | 367 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str()); |
347 } | 368 } |
348 | 369 |
| 370 return conversion_result; |
| 371 } |
| 372 |
| 373 bool UserCollector::ConvertAndEnqueueCrash(int pid, |
| 374 const std::string &exec, |
| 375 bool *out_of_capacity) { |
| 376 FilePath crash_path; |
| 377 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) { |
| 378 LogCollectionError("Unable to find/create process-specific crash path"); |
| 379 return false; |
| 380 } |
| 381 |
| 382 // Directory like /tmp/crash_reporter.1234 which contains the |
| 383 // procfs entries and other temporary files used during conversion. |
| 384 FilePath container_dir = FilePath("/tmp").Append( |
| 385 StringPrintf("crash_reporter.%d", pid)); |
| 386 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid); |
| 387 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core"); |
| 388 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta"); |
| 389 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp"); |
| 390 |
| 391 if (!ConvertCoreToMinidump(pid, container_dir, core_path, |
| 392 minidump_path)) { |
| 393 logger_->LogInfo("Leaving core file at %s due to conversion error", |
| 394 core_path.value().c_str()); |
| 395 return false; |
| 396 } |
| 397 |
| 398 // Here we commit to sending this file. We must not return false |
| 399 // after this point or we will generate a log report as well as a |
| 400 // crash report. |
| 401 WriteCrashMetaData(meta_path, |
| 402 exec, |
| 403 minidump_path.value()); |
| 404 |
349 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { | 405 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) { |
350 file_util::Delete(core_path, false); | 406 file_util::Delete(core_path, false); |
351 } else { | 407 } else { |
352 logger_->LogInfo("Leaving core file at %s", core_path.value().c_str()); | 408 logger_->LogInfo("Leaving core file at %s due to developer image", |
| 409 core_path.value().c_str()); |
353 } | 410 } |
354 | 411 |
355 return conversion_result; | 412 file_util::Delete(container_dir, true); |
| 413 return true; |
356 } | 414 } |
357 | 415 |
358 bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { | 416 bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) { |
359 CHECK(initialized_); | 417 CHECK(initialized_); |
360 std::string exec; | 418 std::string exec; |
361 if (force_exec) { | 419 if (force_exec) { |
362 exec.assign(force_exec); | 420 exec.assign(force_exec); |
363 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { | 421 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) { |
364 // If for some reason we don't have the base name, avoid completely | 422 // If for some reason we don't have the base name, avoid completely |
365 // failing by indicating an unknown name. | 423 // failing by indicating an unknown name. |
366 exec = "unknown"; | 424 exec = "unknown"; |
367 } | 425 } |
368 bool feedback = is_feedback_allowed_function_(); | 426 bool feedback = is_feedback_allowed_function_(); |
369 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", | 427 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)", |
370 exec.c_str(), pid, signal, | 428 exec.c_str(), pid, signal, |
371 feedback ? "handling" : "ignoring"); | 429 feedback ? "handling" : "ignoring - no consent"); |
372 | 430 |
373 if (feedback) { | 431 if (feedback) { |
374 count_crash_function_(); | 432 count_crash_function_(); |
375 | 433 |
376 if (generate_diagnostics_) { | 434 if (generate_diagnostics_) { |
377 return GenerateDiagnostics(pid, exec); | 435 bool out_of_capacity = false; |
| 436 if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) { |
| 437 if (!out_of_capacity) |
| 438 EnqueueCollectionErrorLog(pid, exec); |
| 439 return false; |
| 440 } |
378 } | 441 } |
379 } | 442 } |
| 443 |
380 return true; | 444 return true; |
381 } | 445 } |
OLD | NEW |