chromium/chromecast/crash/linux/dump_info.cc

// 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/i18n/time_formatting.h"
#include "base/logging.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";

// 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 = std::to_string(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);

  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 = 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;
  }

  // Disallow extraneous params
  if (dict->size() != num_params)
    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