File: mock_chromeos_crash_reporter.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (177 lines) | stat: -rw-r--r-- 5,898 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// A simple binary that replicates the crash_reporter / crash_sender
// functionality of Chrome OS for testing purposes. In particular, it has a
// stripped-down version of the parsing logic in
// src/platform2/crash-reporter/chrome_collector.cc, coupled with a simple
// upload function similar to src/platform2/crash-reporter/crash_sender_util.cc
// (but without the compression). This is used in tests to substitute for the
// actual OS crash reporting system.

#include <stdio.h>
#include <stdlib.h>

#include <map>
#include <memory>
#include <string>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_executor.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/error_reporting/constants.h"
#include "net/http/http_status_code.h"
#include "third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h"
#include "url/gurl.h"

namespace {

// Parses the key:length:value triplets similar to
// ChromeCollector::ParseCrashLog. Input is the file descriptor |fd|,
// return is a list of key/value pairs in |values| and a payload in |payload|.
//
// Closes |fd| when done.
bool ParseTriplets(int fd,
                   std::map<std::string, std::string>& values,
                   std::string& payload) {
  base::ScopedFILE stream(fdopen(fd, "rb"));
  if (!stream.get()) {
    PLOG(ERROR) << "fdopen failed!";
    return false;
  }
  std::string data;
  if (!base::ReadStreamToString(stream.get(), &data)) {
    LOG(WARNING) << "Read failed!";
  }

  std::string::size_type pos = 0;
  while (pos < data.size()) {
    std::string::size_type end_of_key = data.find(':', pos);
    if (end_of_key == std::string::npos) {
      LOG(ERROR) << "Incomplete value found, starting at position " << pos;
      return false;
    }

    std::string key = data.substr(pos, end_of_key - pos);
    std::string::size_type end_of_length = data.find(':', end_of_key + 1);
    if (end_of_length == std::string::npos) {
      LOG(ERROR) << "Incomplete length found, starting at position "
                 << (end_of_key + 1);
      return false;
    }

    std::string length_string =
        data.substr(end_of_key + 1, end_of_length - (end_of_key + 1));
    size_t length;
    if (!base::StringToSizeT(length_string, &length)) {
      LOG(ERROR) << "Bad length string '" << length_string << "'";
      return false;
    }

    std::string value = data.substr(end_of_length + 1, length);
    pos = end_of_length + length + 1;

    if (key == kJavaScriptStackKey) {
      payload = std::move(value);
    } else {
      values.emplace(std::move(key), std::move(value));
    }
  }
  return true;
}

// Upload the error report to the provided URL.
bool UploadViaHttp(const std::string& base_url,
                   const std::map<std::string, std::string>& values,
                   const std::string& payload) {
  std::vector<std::string> query_parts;
  for (const auto& kv : values) {
    query_parts.emplace_back(base::StrCat(
        {base::EscapeQueryParamValue(kv.first, /*use_plus=*/false), "=",
         base::EscapeQueryParamValue(kv.second, /*use_plus=*/false)}));
  }
  std::string upload_str =
      base::StrCat({base_url, "?", base::JoinString(query_parts, "&")});

  GURL upload_url(upload_str);
  if (!upload_url.is_valid()) {
    LOG(ERROR) << "Invalid upload_to URL: '" << upload_str << "'";
    return false;
  }

  // Upload using httplib. The normal Chromium way (SimpleURLLoader) needs a lot
  // of browser stuff to be set up before it can be used, so we use the
  // standalone httplib in this test binary.
  std::string host = upload_url.host();
  httplib::Client cli(host.c_str(), upload_url.EffectiveIntPort());
  if (!cli.is_valid()) {
    LOG(ERROR) << "httplib::Client setup error";
    return false;
  }

  std::string path = upload_url.PathForRequest();
  auto response = cli.Post(path.c_str(), payload, "text/plain");
  if (!response) {
    LOG(ERROR) << "No response to Post";
    return false;
  }

  if (response->status != net::HTTP_OK) {
    LOG(ERROR) << "http response " << response->status;
    return false;
  }
  return true;
}

}  // namespace

int main(int argc, char** argv) {
  base::AtExitManager exit_manager;
  base::CommandLine::Init(argc, argv);
  base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();

  constexpr char kFdSwitch[] = "chrome_memfd";
  if (!cmd_line->HasSwitch(kFdSwitch)) {
    LOG(ERROR) << "No --chrome_memfd";
    return EXIT_FAILURE;
  }
  auto fd_string = cmd_line->GetSwitchValueASCII(kFdSwitch);
  int fd;
  if (!base::StringToInt(fd_string, &fd)) {
    LOG(ERROR) << "Can't parse --chrome_memfd '" << fd_string << "' as int";
    return EXIT_FAILURE;
  }

  // Note: This must be a map (not an unordered_map or such) because some unit
  // tests rely on the order of the parameters in the URL string. Until that's
  // fixed, keep the values sorted by key in the URL.
  std::map<std::string, std::string> values;
  std::string payload;
  if (!ParseTriplets(fd, values, payload)) {
    return EXIT_FAILURE;
  }

  constexpr char kUploadSwitch[] = "upload_to";
  if (!cmd_line->HasSwitch(kUploadSwitch)) {
    LOG(ERROR) << "No --upload_to";
    return EXIT_FAILURE;
  }
  std::string base_url = cmd_line->GetSwitchValueASCII(kUploadSwitch);
  if (!UploadViaHttp(base_url, values, payload)) {
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}