blob: 20170bd310af4a2a2b9104b855d5d280755b0ea5 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "crash-reporter/chrome_collector.h"
#include <pcrecpp.h>
#include <string>
#include <vector>
#include <base/file_util.h>
#include <base/logging.h>
#include <base/string_number_conversions.h>
#include <base/string_util.h>
#include "chromeos/process.h"
#include "chromeos/syslog_logging.h"
const char kDefaultMinidumpName[] = "upload_file_minidump";
namespace {
// Extract a string delimited by the given character, from the given offset
// into a source string. Returns false if the string is zero-sized or no
// delimiter was found.
bool GetDelimitedString(const std::string &str, char ch, size_t offset,
std::string *substr) {
size_t at = str.find_first_of(ch, offset);
if (at == std::string::npos || at == offset)
return false;
*substr = str.substr(offset, at - offset);
return true;
}
} //namespace
ChromeCollector::ChromeCollector() {}
ChromeCollector::~ChromeCollector() {}
bool ChromeCollector::HandleCrash(const std::string &file_path,
const std::string &pid_string,
const std::string &uid_string) {
if (!is_feedback_allowed_function_())
return true;
FilePath dir;
uid_t uid = atoi(uid_string.c_str());
pid_t pid = atoi(pid_string.c_str());
if (!GetCreatedCrashDirectoryByEuid(uid, &dir, NULL)) {
LOG(ERROR) << "Can't create crash directory for uid " << uid;
return false;
}
std::string exec;
if (!GetExecutableBaseNameFromPid(pid, &exec)) {
LOG(ERROR) << "Can't get executable name for pid " << pid;
return false;
}
std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
FilePath meta_path = GetCrashPath(dir, dump_basename, "meta");
FilePath minidump_path = GetCrashPath(dir, dump_basename, "dmp");
FilePath log_path = GetCrashPath(dir, dump_basename, "log");
std::string data;
if (!file_util::ReadFileToString(FilePath(file_path), &data)) {
LOG(ERROR) << "Can't read crash log: " << file_path.c_str();
return false;
}
if (!ParseCrashLog(data, dir, minidump_path)) {
LOG(ERROR) << "Failed to parse Chrome's crash log";
return false;
}
if (GetLogContents(FilePath(log_config_path_), exec, log_path))
AddCrashMetaData("log", log_path.value());
// We're done.
WriteCrashMetaData(meta_path, exec, minidump_path.value());
return true;
}
bool ChromeCollector::ParseCrashLog(const std::string &data,
const FilePath &dir,
const FilePath &minidump) {
size_t at = 0;
while (at < data.size()) {
// Look for a : followed by a decimal number, followed by another :
// followed by N bytes of data.
std::string name, size_string;
if (!GetDelimitedString(data, ':', at, &name))
break;
at += name.size() + 1; // Skip the name & : delimiter.
if (!GetDelimitedString(data, ':', at, &size_string))
break;
at += size_string.size() + 1; // Skip the size & : delimiter.
size_t size;
if (!base::StringToSizeT(size_string, &size))
break;
// Data would run past the end, did we get a truncated file?
if (at + size > data.size())
break;
if (name.find("filename") != std::string::npos) {
// File.
// Name will be in a semi-MIME format of
// <descriptive name>"; filename="<name>"
// Descriptive name will be upload_file_minidump for the dump.
std::string desc, filename;
pcrecpp::RE re("(.*)\" *; *filename=\"(.*)\"");
if (!re.FullMatch(name.c_str(), &desc, &filename))
break;
if (filename.compare(kDefaultMinidumpName) == 0) {
// The minidump.
WriteNewFile(minidump, data.c_str() + at, size);
} else {
// Some other file.
FilePath path = GetCrashPath(dir, filename, "other");
if (WriteNewFile(path, data.c_str() + at, size) >= 0) {
std::string value = "@";
value.append(path.value());
AddCrashMetaData(desc, value);
}
}
} else {
// Other attribute.
std::string value_str;
value_str.reserve(size);
// Since metadata is one line/value the values must be escaped properly.
for (size_t i = at; i < at + size; i++) {
switch (data[i]) {
case '"':
case '\\':
value_str.push_back('\\');
value_str.push_back(data[i]);
break;
case '\r':
value_str += "\\r";
break;
case '\n':
value_str += "\\n";
break;
case '\t':
value_str += "\\t";
break;
case '\0':
value_str += "\\0";
break;
default:
value_str.push_back(data[i]);
break;
}
}
AddCrashMetaData(name, value_str);
}
at += size;
}
return at == data.size();
}