Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(211)

Side by Side Diff: user_collector.cc

Issue 4088003: crash-reporter: write conversion failure diagnostics into fake dmp files (Closed) Base URL: http://git.chromium.org/git/crash-reporter.git
Patch Set: Add signature for error logs Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « user_collector.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « user_collector.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698