1 //===-- TraceSessionFileParser.cpp ---------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===/
8
9 #include "TraceSessionFileParser.h"
10 #include "ThreadPostMortemTrace.h"
11
12 #include <sstream>
13
14 #include "lldb/Core/Debugger.h"
15 #include "lldb/Core/Module.h"
16 #include "lldb/Target/Process.h"
17 #include "lldb/Target/Target.h"
18
19 using namespace lldb;
20 using namespace lldb_private;
21 using namespace llvm;
22
NormalizePath(lldb_private::FileSpec & file_spec)23 void TraceSessionFileParser::NormalizePath(lldb_private::FileSpec &file_spec) {
24 if (file_spec.IsRelative())
25 file_spec.PrependPathComponent(m_session_file_dir);
26 }
27
ParseModule(lldb::TargetSP & target_sp,const JSONModule & module)28 Error TraceSessionFileParser::ParseModule(lldb::TargetSP &target_sp,
29 const JSONModule &module) {
30 FileSpec system_file_spec(module.system_path);
31 NormalizePath(system_file_spec);
32
33 FileSpec local_file_spec(module.file.hasValue() ? *module.file
34 : module.system_path);
35 NormalizePath(local_file_spec);
36
37 ModuleSpec module_spec;
38 module_spec.GetFileSpec() = local_file_spec;
39 module_spec.GetPlatformFileSpec() = system_file_spec;
40
41 if (module.uuid.hasValue())
42 module_spec.GetUUID().SetFromStringRef(*module.uuid);
43
44 Status error;
45 ModuleSP module_sp =
46 target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
47
48 if (error.Fail())
49 return error.ToError();
50
51 bool load_addr_changed = false;
52 module_sp->SetLoadAddress(*target_sp, module.load_address.value, false,
53 load_addr_changed);
54 return llvm::Error::success();
55 }
56
CreateJSONError(json::Path::Root & root,const json::Value & value)57 Error TraceSessionFileParser::CreateJSONError(json::Path::Root &root,
58 const json::Value &value) {
59 std::string err;
60 raw_string_ostream os(err);
61 root.printErrorContext(value, os);
62 return createStringError(
63 std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
64 toString(root.getError()).c_str(), os.str().c_str(), m_schema.data());
65 }
66
BuildSchema(StringRef plugin_schema)67 std::string TraceSessionFileParser::BuildSchema(StringRef plugin_schema) {
68 std::ostringstream schema_builder;
69 schema_builder << "{\n \"trace\": ";
70 schema_builder << plugin_schema.data() << ",";
71 schema_builder << R"(
72 "processes": [
73 {
74 "pid": integer,
75 "triple": string, // llvm-triple
76 "threads": [
77 {
78 "tid": integer,
79 "traceFile": string
80 }
81 ],
82 "modules": [
83 {
84 "systemPath": string, // original path of the module at runtime
85 "file"?: string, // copy of the file if not available at "systemPath"
86 "loadAddress": string, // string address in hex or decimal form
87 "uuid"?: string,
88 }
89 ]
90 }
91 ]
92 // Notes:
93 // All paths are either absolute or relative to the session file.
94 }
95 )";
96 return schema_builder.str();
97 }
98
99 ThreadPostMortemTraceSP
ParseThread(ProcessSP & process_sp,const JSONThread & thread)100 TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
101 const JSONThread &thread) {
102 lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
103
104 FileSpec trace_file(thread.trace_file);
105 NormalizePath(trace_file);
106
107 ThreadPostMortemTraceSP thread_sp =
108 std::make_shared<ThreadPostMortemTrace>(*process_sp, tid, trace_file);
109 process_sp->GetThreadList().AddThread(thread_sp);
110 return thread_sp;
111 }
112
113 Expected<TraceSessionFileParser::ParsedProcess>
ParseProcess(const JSONProcess & process)114 TraceSessionFileParser::ParseProcess(const JSONProcess &process) {
115 TargetSP target_sp;
116 Status error = m_debugger.GetTargetList().CreateTarget(
117 m_debugger, /*user_exe_path*/ StringRef(), process.triple,
118 eLoadDependentsNo,
119 /*platform_options*/ nullptr, target_sp);
120
121 if (!target_sp)
122 return error.ToError();
123
124 ParsedProcess parsed_process;
125 parsed_process.target_sp = target_sp;
126
127 ProcessSP process_sp = target_sp->CreateProcess(
128 /*listener*/ nullptr, "trace",
129 /*crash_file*/ nullptr,
130 /*can_connect*/ false);
131
132 process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
133
134 for (const JSONThread &thread : process.threads)
135 parsed_process.threads.push_back(ParseThread(process_sp, thread));
136
137 for (const JSONModule &module : process.modules)
138 if (Error err = ParseModule(target_sp, module))
139 return std::move(err);
140
141 if (!process.threads.empty())
142 process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
143
144 // We invoke DidAttach to create a correct stopped state for the process and
145 // its threads.
146 ArchSpec process_arch;
147 process_sp->DidAttach(process_arch);
148
149 return parsed_process;
150 }
151
152 Expected<std::vector<TraceSessionFileParser::ParsedProcess>>
ParseCommonSessionFile(const JSONTraceSessionBase & session)153 TraceSessionFileParser::ParseCommonSessionFile(
154 const JSONTraceSessionBase &session) {
155 std::vector<ParsedProcess> parsed_processes;
156
157 auto onError = [&]() {
158 // Delete all targets that were created so far in case of failures
159 for (ParsedProcess &parsed_process : parsed_processes)
160 m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
161 };
162
163 for (const JSONProcess &process : session.processes) {
164 if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
165 parsed_processes.push_back(std::move(*parsed_process));
166 else {
167 onError();
168 return parsed_process.takeError();
169 }
170 }
171 return parsed_processes;
172 }
173
174 namespace llvm {
175 namespace json {
176
fromJSON(const Value & value,TraceSessionFileParser::JSONAddress & address,Path path)177 bool fromJSON(const Value &value, TraceSessionFileParser::JSONAddress &address,
178 Path path) {
179 Optional<StringRef> s = value.getAsString();
180 if (s.hasValue() && !s->getAsInteger(0, address.value))
181 return true;
182
183 path.report("expected numeric string");
184 return false;
185 }
186
fromJSON(const Value & value,TraceSessionFileParser::JSONModule & module,Path path)187 bool fromJSON(const Value &value, TraceSessionFileParser::JSONModule &module,
188 Path path) {
189 ObjectMapper o(value, path);
190 return o && o.map("systemPath", module.system_path) &&
191 o.map("file", module.file) &&
192 o.map("loadAddress", module.load_address) &&
193 o.map("uuid", module.uuid);
194 }
195
fromJSON(const Value & value,TraceSessionFileParser::JSONThread & thread,Path path)196 bool fromJSON(const Value &value, TraceSessionFileParser::JSONThread &thread,
197 Path path) {
198 ObjectMapper o(value, path);
199 return o && o.map("tid", thread.tid) && o.map("traceFile", thread.trace_file);
200 }
201
fromJSON(const Value & value,TraceSessionFileParser::JSONProcess & process,Path path)202 bool fromJSON(const Value &value, TraceSessionFileParser::JSONProcess &process,
203 Path path) {
204 ObjectMapper o(value, path);
205 return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
206 o.map("threads", process.threads) && o.map("modules", process.modules);
207 }
208
fromJSON(const Value & value,TraceSessionFileParser::JSONTracePluginSettings & plugin_settings,Path path)209 bool fromJSON(const Value &value,
210 TraceSessionFileParser::JSONTracePluginSettings &plugin_settings,
211 Path path) {
212 ObjectMapper o(value, path);
213 return o && o.map("type", plugin_settings.type);
214 }
215
fromJSON(const Value & value,TraceSessionFileParser::JSONTraceSessionBase & session,Path path)216 bool fromJSON(const Value &value,
217 TraceSessionFileParser::JSONTraceSessionBase &session,
218 Path path) {
219 ObjectMapper o(value, path);
220 return o && o.map("processes", session.processes);
221 }
222
223 } // namespace json
224 } // namespace llvm
225