xref: /aosp_15_r20/system/core/debuggerd/tombstone_symbolize.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "tombstone_symbolize.h"
18 
19 #include <fcntl.h>
20 #include <inttypes.h>
21 #include <unistd.h>
22 
23 #include <string>
24 #include <vector>
25 
26 #include "android-base/stringprintf.h"
27 #include "android-base/unique_fd.h"
28 
29 #include "tombstone.pb.h"
30 
31 using android::base::StringPrintf;
32 using android::base::unique_fd;
33 
Start(const std::vector<std::string> & debug_file_directories)34 bool Symbolizer::Start(const std::vector<std::string>& debug_file_directories) {
35   unique_fd parent_in, parent_out, child_in, child_out;
36   if (!Pipe(&parent_in, &child_out) || !Pipe(&child_in, &parent_out)) {
37     return false;
38   }
39 
40   std::vector<const char *> args;
41   args.push_back("llvm-symbolizer");
42   for (const std::string &dir : debug_file_directories) {
43     args.push_back("--debug-file-directory");
44     args.push_back(dir.c_str());
45   }
46   args.push_back(0);
47 
48   int pid = fork();
49   if (pid == -1) {
50     return false;
51   } else if (pid == 0) {
52     parent_in.reset();
53     parent_out.reset();
54 
55     dup2(child_in.get(), STDIN_FILENO);
56     dup2(child_out.get(), STDOUT_FILENO);
57 
58     execvp("llvm-symbolizer", const_cast<char *const *>(args.data()));
59 
60     fprintf(stderr, "unable to start llvm-symbolizer: %s\n", strerror(errno));
61     _exit(1);
62   } else {
63     child_in.reset();
64     child_out.reset();
65 
66     // TODO: Check that llvm-symbolizer started up successfully.
67     // There used to be an easy way to do this, but it was removed in:
68     // https://github.com/llvm/llvm-project/commit/1792852f86dc75efa1f44d46b1a0daf386d64afa
69 
70     in_fd = std::move(parent_in);
71     out_fd = std::move(parent_out);
72     return true;
73   }
74 }
75 
read_response()76 std::string Symbolizer::read_response() {
77   std::string resp;
78 
79   while (resp.size() < 2 || resp[resp.size() - 2] != '\n' || resp[resp.size() - 1] != '\n') {
80     char buf[4096];
81     ssize_t size = read(in_fd, buf, 4096);
82     if (size <= 0) {
83       return "";
84     }
85     resp.append(buf, size);
86   }
87 
88   return resp;
89 }
90 
SymbolizeCode(std::string path,uint64_t rel_pc)91 std::vector<Symbolizer::Frame> Symbolizer::SymbolizeCode(std::string path, uint64_t rel_pc) {
92   std::string request = StringPrintf("CODE %s 0x%" PRIx64 "\n", path.c_str(), rel_pc);
93   if (write(out_fd, request.c_str(), request.size()) != static_cast<ssize_t>(request.size())) {
94     return {};
95   }
96 
97   std::string response = read_response();
98   if (response.empty()) {
99     return {};
100   }
101 
102   std::vector<Symbolizer::Frame> frames;
103 
104   size_t frame_start = 0;
105   while (frame_start < response.size() - 1) {
106     Symbolizer::Frame frame;
107 
108     size_t second_line_start = response.find('\n', frame_start) + 1;
109     if (second_line_start == std::string::npos + 1) {
110       return {};
111     }
112 
113     size_t third_line_start = response.find('\n', second_line_start) + 1;
114     if (third_line_start == std::string::npos + 1) {
115       return {};
116     }
117 
118     frame.function_name = response.substr(frame_start, second_line_start - frame_start - 1);
119 
120     size_t column_number_start = response.rfind(':', third_line_start);
121     if (column_number_start == std::string::npos) {
122       return {};
123     }
124 
125     size_t line_number_start = response.rfind(':', column_number_start - 1);
126     if (line_number_start == std::string::npos) {
127       return {};
128     }
129 
130     frame.file = response.substr(second_line_start, line_number_start - second_line_start);
131 
132     errno = 0;
133     frame.line = strtoull(response.c_str() + line_number_start + 1, 0, 10);
134     frame.column = strtoull(response.c_str() + column_number_start + 1, 0, 10);
135     if (errno != 0) {
136       return {};
137     }
138 
139     frames.push_back(frame);
140 
141     frame_start = third_line_start;
142   }
143 
144   if (frames.size() == 1 && frames[0].file == "??") {
145     return {};
146   }
147 
148   return frames;
149 }
150 
symbolize_backtrace_frame(const BacktraceFrame & frame,Symbolizer & sym)151 void symbolize_backtrace_frame(const BacktraceFrame& frame, Symbolizer& sym) {
152   if (frame.build_id().empty()) {
153     return;
154   }
155 
156   for (Symbolizer::Frame f : sym.SymbolizeCode("BUILDID:" + frame.build_id(), frame.rel_pc())) {
157     printf("          %s:%" PRId64 ":%" PRId64 " (%s)\n", f.file.c_str(), f.line, f.column,
158            f.function_name.c_str());
159   }
160 }
161