| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromecast/crash/linux/dump_info.h" |
| |
| #include <errno.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| |
| #include <string_view> |
| |
| #include "base/compiler_specific.h" |
| #include "base/i18n/time_formatting.h" |
| #include "base/logging.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| |
| namespace chromecast { |
| |
| namespace { |
| |
| const int kNumRequiredParams = 4; |
| |
| const char kNameKey[] = "name"; |
| const char kDumpTimeKey[] = "dump_time"; |
| const char kDumpKey[] = "dump"; |
| const char kUptimeKey[] = "uptime"; |
| const char kLogfileKey[] = "logfile"; |
| const char kAttachmentsKey[] = "attachments"; |
| const char kSuffixKey[] = "suffix"; |
| const char kPrevAppNameKey[] = "prev_app_name"; |
| const char kCurAppNameKey[] = "cur_app_name"; |
| const char kLastAppNameKey[] = "last_app_name"; |
| const char kReleaseVersionKey[] = "release_version"; |
| const char kBuildNumberKey[] = "build_number"; |
| const char kReasonKey[] = "reason"; |
| const char kStadiaSessionIdKey[] = "stadia_session_id"; |
| const char kCrashProductNameKey[] = "crash_product_name"; |
| const char kExecNameKey[] = "exec_name"; |
| const char kSignatureKey[] = "signature"; |
| const char kExtraInfoKey[] = "extra_info"; |
| |
| // CastLite specific data |
| const char kComments[] = "comments"; |
| const char kJsEngine[] = "js_engine"; |
| const char kJsBuildLabel[] = "js_build_label"; |
| const char kJsExceptionCategory[] = "js_exception_category"; |
| const char kJsExceptionDetails[] = "js_exception_details"; |
| const char kJsExceptionSignature[] = "js_exception_signature"; |
| const char kJsErrorAppKey[] = "js_error_app"; |
| const char kPreviousLogFileKey[] = "previous_logfile"; |
| const char kBackgroundAppsKey[] = "background_apps"; |
| const char kServerUrl[] = "server_url"; |
| |
| // Convenience wrapper around Value::Dict::FindString(), for easier use in if |
| // statements. If `key` is a string in `dict`, writes it to `out` and returns |
| // true. Leaves `out` alone and returns false otherwise. |
| bool FindString(const base::Value::Dict& dict, |
| std::string_view key, |
| std::string& out) { |
| const std::string* value = dict.FindString(key); |
| if (!value) |
| return false; |
| out = *value; |
| return true; |
| } |
| |
| } // namespace |
| |
| DumpInfo::DumpInfo(const base::Value* entry) : valid_(ParseEntry(entry)) {} |
| |
| DumpInfo::DumpInfo(const std::string& crashed_process_dump, |
| const std::string& crashed_process_logfile, |
| const base::Time& dump_time, |
| const MinidumpParams& params, |
| const std::vector<std::string>* attachments) |
| : crashed_process_dump_(crashed_process_dump), |
| logfile_(crashed_process_logfile), |
| dump_time_(dump_time), |
| params_(params), |
| valid_(true) { |
| if (attachments) { |
| attachments_ = *attachments; |
| } |
| } |
| |
| DumpInfo::~DumpInfo() {} |
| |
| base::Value DumpInfo::GetAsValue() const { |
| base::Value::Dict result; |
| |
| result.Set(kDumpTimeKey, base::UnlocalizedTimeFormatWithPattern( |
| dump_time_, "yyyy-MM-dd HH:mm:ss")); |
| |
| result.Set(kDumpKey, crashed_process_dump_); |
| std::string uptime = base::NumberToString(params_.process_uptime); |
| result.Set(kUptimeKey, uptime); |
| result.Set(kLogfileKey, logfile_); |
| |
| base::Value::List attachments_list; |
| for (const auto& attachment : attachments_) { |
| attachments_list.Append(attachment); |
| } |
| result.Set(kAttachmentsKey, std::move(attachments_list)); |
| result.Set(kSuffixKey, params_.suffix); |
| result.Set(kPrevAppNameKey, params_.previous_app_name); |
| result.Set(kCurAppNameKey, params_.current_app_name); |
| result.Set(kLastAppNameKey, params_.last_app_name); |
| result.Set(kReleaseVersionKey, params_.cast_release_version); |
| result.Set(kBuildNumberKey, params_.cast_build_number); |
| result.Set(kReasonKey, params_.reason); |
| result.Set(kStadiaSessionIdKey, params_.stadia_session_id); |
| result.Set(kExecNameKey, params_.exec_name); |
| result.Set(kSignatureKey, params_.signature); |
| result.Set(kExtraInfoKey, params_.extra_info); |
| result.Set(kCrashProductNameKey, params_.crash_product_name); |
| result.Set(kComments, params_.comments); |
| result.Set(kJsEngine, params_.js_engine); |
| result.Set(kJsBuildLabel, params_.js_build_label); |
| result.Set(kJsExceptionCategory, params_.js_exception_category); |
| result.Set(kJsExceptionDetails, params_.js_exception_details); |
| result.Set(kJsExceptionSignature, params_.js_exception_signature); |
| result.Set(kJsErrorAppKey, params_.js_error_app); |
| result.Set(kPreviousLogFileKey, params_.previous_logfile); |
| result.Set(kBackgroundAppsKey, params_.background_apps); |
| result.Set(kServerUrl, params_.server_url); |
| |
| return base::Value(std::move(result)); |
| } |
| |
| bool DumpInfo::ParseEntry(const base::Value* entry) { |
| valid_ = false; |
| |
| if (!entry) |
| return false; |
| |
| const base::Value::Dict* dict = entry->GetIfDict(); |
| if (!dict) |
| return false; |
| |
| // Extract required fields. |
| std::string dump_time; |
| if (!FindString(*dict, kDumpTimeKey, dump_time)) |
| return false; |
| if (!SetDumpTimeFromString(dump_time)) |
| return false; |
| |
| if (!FindString(*dict, kDumpKey, crashed_process_dump_)) |
| return false; |
| |
| std::string uptime; |
| if (!FindString(*dict, kUptimeKey, uptime)) |
| return false; |
| errno = 0; |
| params_.process_uptime = UNSAFE_TODO(strtoull(uptime.c_str(), nullptr, 0)); |
| if (errno != 0) |
| return false; |
| |
| if (!FindString(*dict, kLogfileKey, logfile_)) |
| return false; |
| size_t num_params = kNumRequiredParams; |
| |
| // Extract all other optional fields. |
| const base::Value::List* attachments_list = dict->FindList(kAttachmentsKey); |
| if (attachments_list) { |
| ++num_params; |
| for (const auto& attachment : *attachments_list) { |
| attachments_.push_back(attachment.GetString()); |
| } |
| } |
| |
| std::string unused_process_name; |
| if (FindString(*dict, kNameKey, unused_process_name)) |
| ++num_params; |
| if (FindString(*dict, kSuffixKey, params_.suffix)) |
| ++num_params; |
| if (FindString(*dict, kPrevAppNameKey, params_.previous_app_name)) |
| ++num_params; |
| if (FindString(*dict, kCurAppNameKey, params_.current_app_name)) |
| ++num_params; |
| if (FindString(*dict, kLastAppNameKey, params_.last_app_name)) |
| ++num_params; |
| if (FindString(*dict, kReleaseVersionKey, params_.cast_release_version)) |
| ++num_params; |
| if (FindString(*dict, kBuildNumberKey, params_.cast_build_number)) |
| ++num_params; |
| if (FindString(*dict, kReasonKey, params_.reason)) |
| ++num_params; |
| if (FindString(*dict, kStadiaSessionIdKey, params_.stadia_session_id)) |
| ++num_params; |
| if (FindString(*dict, kExecNameKey, params_.exec_name)) |
| ++num_params; |
| if (FindString(*dict, kSignatureKey, params_.signature)) |
| ++num_params; |
| if (FindString(*dict, kExtraInfoKey, params_.extra_info)) |
| ++num_params; |
| if (FindString(*dict, kCrashProductNameKey, params_.crash_product_name)) |
| ++num_params; |
| if (FindString(*dict, kComments, params_.comments)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kJsEngine, params_.js_engine)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kJsBuildLabel, params_.js_build_label)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kJsExceptionCategory, params_.js_exception_category)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kJsExceptionDetails, params_.js_exception_details)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kJsExceptionSignature, |
| params_.js_exception_signature)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kJsErrorAppKey, params_.js_error_app)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kPreviousLogFileKey, params_.previous_logfile)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kBackgroundAppsKey, params_.background_apps)) { |
| ++num_params; |
| } |
| if (FindString(*dict, kServerUrl, params_.server_url)) { |
| ++num_params; |
| } |
| |
| // Disallow extraneous params |
| if (dict->size() != num_params) { |
| LOG(ERROR) << "Failed to parse DumpInfo: missing required fields"; |
| return false; |
| } |
| |
| valid_ = true; |
| return true; |
| } |
| |
| bool DumpInfo::SetDumpTimeFromString(const std::string& timestr) { |
| if (base::Time::FromString(timestr.c_str(), &dump_time_)) { |
| return true; |
| } |
| |
| LOG(INFO) << "Failed to convert dump time invalid"; |
| return false; |
| } |
| |
| } // namespace chromecast |