Repo-Init
 
Loading...
Searching...
No Matches
Tracer.cpp
Go to the documentation of this file.
1#include "utils/Tracer.hpp"
2
3#include "Version.h"
4
5#include "client/crash_report_database.h"
6#include "client/crashpad_client.h"
7#include "client/settings.h"
8#include <spdlog/spdlog.h>
9
10#include <algorithm>
11#include <array>
12#include <fstream>
13#include <sstream>
14
15#include <sys/socket.h>
16#include <sys/stat.h>
17#include <sys/types.h>
18#include <unistd.h>
19
20constexpr int SLEEP_INTERVAL_MS = 50;
21
23{
24 // Path to crashpad executable
25 const base::FilePath handler(_handlerPath);
26
27 // Must be writable or crashpad_handler will crash
28 const base::FilePath reportsDir(_reportPath);
29 const base::FilePath metricsDir(_reportPath);
30
31 // Initialize Crashpad database
32 auto database = crashpad::CrashReportDatabase::Initialize(reportsDir);
33 if (database == nullptr)
34 {
35 throw std::ios_base::failure("Can't initialize crash report database");
36 }
37
38 // Enable automated crash uploads
39 auto *settings = database->GetSettings();
40 if (settings == nullptr)
41 {
42 throw std::ios_base::failure("Can't get crash report database settings");
43 }
44 settings->SetUploadsEnabled(true);
45
46 // Start crash handler
47 if (!_clientHandler->StartHandler(handler, reportsDir, metricsDir, _serverPath, _serverProxy, _annotations,
48 {"--no-rate-limit"}, true, false, _attachments))
49 {
50 throw std::ios_base::failure("Can't start crash handler");
51 }
52
53 _thread = std::make_unique<std::thread>(&Tracer::threadFunc, this);
54}
55
56void Tracer::threadFunc() noexcept
57{
58 while (!_shouldStop._M_i)
59 {
60 try
61 {
62 if (!isRunning())
63 {
64 // NOTICE: Worked before but currently restarting the handler raises an error
65 // due to changes in the crashpad library
66 restart();
67 spdlog::warn("Crashpad handler restarted");
68 }
69 if (_checkFlag)
70 {
71 _checkFlag->test_and_set();
72 }
73 }
74 catch (const std::exception &e)
75 {
76 spdlog::error("Crashpad failed: {}", e.what());
77 }
78 std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_INTERVAL_MS));
79 }
80}
81
82bool Tracer::checkPidIsRunning(pid_t processId) { return kill(processId, 0) == 0; }
83
85{
86 int error = 0;
87 socklen_t len = sizeof(error);
88
89 char buff = 0;
90 const int result = getsockopt(sockId, SOL_SOCKET, SO_ERROR, &error, &len);
91 return result == 0 && error == 0 && recv(sockId, &buff, 1, MSG_PEEK | MSG_DONTWAIT) != 0;
92}
93
95{
96 std::array<char, FILENAME_MAX> pathBuffer{'\0'};
97 auto bytes = readlink("/proc/self/exe", pathBuffer.data(), sizeof(pathBuffer));
98
99 auto path = std::string(pathBuffer.data(), bytes == -1 ? 0 : static_cast<size_t>(bytes));
100 auto lastDelimPos = path.find_last_of('/');
101 return (lastDelimPos == std::string::npos) ? "" : path.substr(0, lastDelimPos);
102}
103
105{
106 int sockId{-1};
107 pid_t processId{-1};
108
109 if (!crashpad::CrashpadClient::GetHandlerSocket(&sockId, &processId))
110 {
111 return false;
112 }
113
114 if (sockId >= 0 && !checkSocketIsRunning(sockId))
115 {
116 return false;
117 }
118 if (processId > 0 && !checkPidIsRunning(processId))
119 {
120 return false;
121 }
122 return true;
123}
124
126{
127 if (!isRunning())
128 {
129 startHandler();
130 }
131}
132
133void Tracer::dumpSharedLibraryInfo(const std::string &filePath)
134{
135 // Open the output file
136 std::ofstream ofile(filePath);
137 if (!ofile.is_open())
138 {
139 throw std::invalid_argument("Can't open file: " + filePath);
140 }
141
142 // Get the shared library information
143 std::ifstream maps("/proc/self/maps");
144
145 std::string line;
146 while (std::getline(maps, line))
147 {
148 // The format of each line is: address perms offset dev inode pathname
149 // We only care about the address and the pathname, which are the first and last fields
150 std::istringstream iss(line);
151 std::string address;
152 std::string perms;
153 std::string offset;
154 std::string dev;
155 std::string inode;
156 std::string pathname;
157 iss >> address >> perms >> offset >> dev >> inode >> pathname;
158
159 // We only want the shared libraries, which have the .so extension and the read and execute permissions
160 if (pathname.find(".so") != std::string::npos && perms.find("r-x") != std::string::npos)
161 {
162 // The address field is in the form of start-end, we only need the start address
163 const std::string start = address.substr(0, address.find('-'));
164
165 // Convert the start address from hexadecimal string to unsigned long
166 const unsigned long addr = std::stoul(start, nullptr, 16);
167
168 ofile << pathname << " " << addr << '\n';
169 }
170 }
171}
172
173Tracer::Tracer(std::shared_ptr<std::atomic_flag> checkFlag, std::string serverPath, std::string serverProxy,
174 const std::string &crashpadHandlerPath, const std::string &reportPath,
175 std::vector<base::FilePath> attachments)
176 : _checkFlag(std::move(checkFlag)), _serverPath(std::move(serverPath)), _serverProxy(std::move(serverProxy)),
177 _attachments(std::move(attachments))
178{
179 auto selfDir = getSelfExecutableDir();
180
181 _handlerPath = crashpadHandlerPath.empty() ? selfDir + "/crashpad_handler" : crashpadHandlerPath;
182 _reportPath = reportPath.empty() ? selfDir : reportPath;
183 _clientHandler = std::make_unique<crashpad::CrashpadClient>();
184
185 _annotations = std::map<std::string, std::string>(
186 {{"name", PROJECT_NAME},
187 {"version", PROJECT_FULL_REVISION},
188 {"build_info", PROJECT_BUILD_DATE + std::string(" ") + PROJECT_BUILD_TIME + std::string(" ") + BUILD_TYPE},
189 {"compiler_info", COMPILER_NAME + std::string(" ") + COMPILER_VERSION}});
190
191 // Dump shared library information and add as attachment
192 dumpSharedLibraryInfo(_reportPath + "/shared_libs.txt");
193 _attachments.emplace_back(_reportPath + "/shared_libs.txt");
194
195 startHandler();
196}
197
199{
200 _shouldStop._M_i = true;
201 if (_thread && _thread->joinable())
202 {
203 _thread->join();
204 }
205}
constexpr int SLEEP_INTERVAL_MS
constexpr int SLEEP_INTERVAL_MS
Definition Tracer.cpp:20
static bool checkSocketIsRunning(int sockId)
Definition Tracer.cpp:84
std::atomic_flag _shouldStop
Definition Tracer.hpp:13
std::string _reportPath
Definition Tracer.hpp:21
static void dumpSharedLibraryInfo(const std::string &filePath)
Definition Tracer.cpp:133
void threadFunc() noexcept
Definition Tracer.cpp:56
void restart()
Definition Tracer.cpp:125
static bool isRunning()
Definition Tracer.cpp:104
void startHandler()
Definition Tracer.cpp:22
std::unique_ptr< crashpad::CrashpadClient > _clientHandler
Definition Tracer.hpp:22
std::string _serverPath
Definition Tracer.hpp:16
std::string _serverProxy
Definition Tracer.hpp:17
~Tracer()
Definition Tracer.cpp:198
std::vector< base::FilePath > _attachments
Definition Tracer.hpp:20
std::shared_ptr< std::atomic_flag > _checkFlag
Definition Tracer.hpp:14
static std::string getSelfExecutableDir()
Definition Tracer.cpp:94
std::map< std::string, std::string > _annotations
Definition Tracer.hpp:19
std::string _handlerPath
Definition Tracer.hpp:18
std::unique_ptr< std::thread > _thread
Definition Tracer.hpp:12
Tracer(std::shared_ptr< std::atomic_flag > checkFlag, std::string serverPath="", std::string serverProxy="", const std::string &crashpadHandlerPath="", const std::string &reportPath="", std::vector< base::FilePath > attachments={})
Definition Tracer.cpp:173
static bool checkPidIsRunning(pid_t processId)
Definition Tracer.cpp:82