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 23 void TraceSessionFileParser::NormalizePath(lldb_private::FileSpec &file_spec) { 24 if (file_spec.IsRelative()) 25 file_spec.PrependPathComponent(m_session_file_dir); 26 } 27 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 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 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 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> 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>> 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 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 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 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 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 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 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