• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "lldb/Target/TraceSessionFileParser.h"
10 
11 #include <sstream>
12 
13 #include "lldb/Core/Debugger.h"
14 #include "lldb/Core/Module.h"
15 #include "lldb/Target/Process.h"
16 #include "lldb/Target/Target.h"
17 #include "lldb/Target/ThreadTrace.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 
ParseThread(ProcessSP & process_sp,const JSONThread & thread)99 ThreadTraceSP TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
100                                                   const JSONThread &thread) {
101   lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
102 
103   FileSpec trace_file(thread.trace_file);
104   NormalizePath(trace_file);
105 
106   ThreadTraceSP thread_sp =
107       std::make_shared<ThreadTrace>(*process_sp, tid, trace_file);
108   process_sp->GetThreadList().AddThread(thread_sp);
109   return thread_sp;
110 }
111 
112 Expected<TraceSessionFileParser::ParsedProcess>
ParseProcess(const JSONProcess & process)113 TraceSessionFileParser::ParseProcess(const JSONProcess &process) {
114   TargetSP target_sp;
115   Status error = m_debugger.GetTargetList().CreateTarget(
116       m_debugger, /*user_exe_path*/ StringRef(), process.triple,
117       eLoadDependentsNo,
118       /*platform_options*/ nullptr, target_sp);
119 
120   if (!target_sp)
121     return error.ToError();
122 
123   ParsedProcess parsed_process;
124   parsed_process.target_sp = target_sp;
125 
126   m_debugger.GetTargetList().SetSelectedTarget(target_sp.get());
127 
128   ProcessSP process_sp = target_sp->CreateProcess(
129       /*listener*/ nullptr, "trace",
130       /*crash_file*/ nullptr,
131       /*can_connect*/ false);
132 
133   process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
134 
135   for (const JSONThread &thread : process.threads)
136     parsed_process.threads.push_back(ParseThread(process_sp, thread));
137 
138   for (const JSONModule &module : process.modules)
139     if (Error err = ParseModule(target_sp, module))
140       return std::move(err);
141 
142   if (!process.threads.empty())
143     process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
144 
145   // We invoke DidAttach to create a correct stopped state for the process and
146   // its threads.
147   ArchSpec process_arch;
148   process_sp->DidAttach(process_arch);
149 
150   return parsed_process;
151 }
152 
153 Expected<std::vector<TraceSessionFileParser::ParsedProcess>>
ParseCommonSessionFile(const JSONTraceSessionBase & session)154 TraceSessionFileParser::ParseCommonSessionFile(
155     const JSONTraceSessionBase &session) {
156   std::vector<ParsedProcess> parsed_processes;
157 
158   auto onError = [&]() {
159     // Delete all targets that were created so far in case of failures
160     for (ParsedProcess &parsed_process : parsed_processes)
161       m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
162   };
163 
164   for (const JSONProcess &process : session.processes) {
165     if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
166       parsed_processes.push_back(std::move(*parsed_process));
167     else {
168       onError();
169       return parsed_process.takeError();
170     }
171   }
172   return parsed_processes;
173 }
174 
175 namespace llvm {
176 namespace json {
177 
fromJSON(const Value & value,TraceSessionFileParser::JSONAddress & address,Path path)178 bool fromJSON(const Value &value, TraceSessionFileParser::JSONAddress &address,
179               Path path) {
180   Optional<StringRef> s = value.getAsString();
181   if (s.hasValue() && !s->getAsInteger(0, address.value))
182     return true;
183 
184   path.report("expected numeric string");
185   return false;
186 }
187 
fromJSON(const Value & value,TraceSessionFileParser::JSONModule & module,Path path)188 bool fromJSON(const Value &value, TraceSessionFileParser::JSONModule &module,
189               Path path) {
190   ObjectMapper o(value, path);
191   return o && o.map("systemPath", module.system_path) &&
192          o.map("file", module.file) &&
193          o.map("loadAddress", module.load_address) &&
194          o.map("uuid", module.uuid);
195 }
196 
fromJSON(const Value & value,TraceSessionFileParser::JSONThread & thread,Path path)197 bool fromJSON(const Value &value, TraceSessionFileParser::JSONThread &thread,
198               Path path) {
199   ObjectMapper o(value, path);
200   return o && o.map("tid", thread.tid) && o.map("traceFile", thread.trace_file);
201 }
202 
fromJSON(const Value & value,TraceSessionFileParser::JSONProcess & process,Path path)203 bool fromJSON(const Value &value, TraceSessionFileParser::JSONProcess &process,
204               Path path) {
205   ObjectMapper o(value, path);
206   return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
207          o.map("threads", process.threads) && o.map("modules", process.modules);
208 }
209 
fromJSON(const Value & value,TraceSessionFileParser::JSONTracePluginSettings & plugin_settings,Path path)210 bool fromJSON(const Value &value,
211               TraceSessionFileParser::JSONTracePluginSettings &plugin_settings,
212               Path path) {
213   ObjectMapper o(value, path);
214   return o && o.map("type", plugin_settings.type);
215 }
216 
fromJSON(const Value & value,TraceSessionFileParser::JSONTraceSessionBase & session,Path path)217 bool fromJSON(const Value &value,
218               TraceSessionFileParser::JSONTraceSessionBase &session,
219               Path path) {
220   ObjectMapper o(value, path);
221   return o && o.map("processes", session.processes);
222 }
223 
224 } // namespace json
225 } // namespace llvm
226