1*f6aab3d8Srobert //===-- TraceIntelPTJSONStructs.cpp ---------------------------------------===//
2*f6aab3d8Srobert //
3*f6aab3d8Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*f6aab3d8Srobert // See https://llvm.org/LICENSE.txt for license information.
5*f6aab3d8Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*f6aab3d8Srobert //
7*f6aab3d8Srobert //===----------------------------------------------------------------------===//
8*f6aab3d8Srobert 
9*f6aab3d8Srobert #include "TraceIntelPTJSONStructs.h"
10*f6aab3d8Srobert #include "llvm/Support/JSON.h"
11*f6aab3d8Srobert #include <optional>
12*f6aab3d8Srobert #include <string>
13*f6aab3d8Srobert 
14*f6aab3d8Srobert using namespace lldb;
15*f6aab3d8Srobert using namespace lldb_private;
16*f6aab3d8Srobert using namespace lldb_private::trace_intel_pt;
17*f6aab3d8Srobert using namespace llvm;
18*f6aab3d8Srobert using namespace llvm::json;
19*f6aab3d8Srobert 
20*f6aab3d8Srobert namespace lldb_private {
21*f6aab3d8Srobert namespace trace_intel_pt {
22*f6aab3d8Srobert 
23*f6aab3d8Srobert std::optional<std::vector<lldb::cpu_id_t>>
GetCpuIds()24*f6aab3d8Srobert JSONTraceBundleDescription::GetCpuIds() {
25*f6aab3d8Srobert   if (!cpus)
26*f6aab3d8Srobert     return std::nullopt;
27*f6aab3d8Srobert   std::vector<lldb::cpu_id_t> cpu_ids;
28*f6aab3d8Srobert   for (const JSONCpu &cpu : *cpus)
29*f6aab3d8Srobert     cpu_ids.push_back(cpu.id);
30*f6aab3d8Srobert   return cpu_ids;
31*f6aab3d8Srobert }
32*f6aab3d8Srobert 
toJSON(const JSONModule & module)33*f6aab3d8Srobert json::Value toJSON(const JSONModule &module) {
34*f6aab3d8Srobert   json::Object json_module;
35*f6aab3d8Srobert   json_module["systemPath"] = module.system_path;
36*f6aab3d8Srobert   if (module.file)
37*f6aab3d8Srobert     json_module["file"] = *module.file;
38*f6aab3d8Srobert   json_module["loadAddress"] = toJSON(module.load_address, true);
39*f6aab3d8Srobert   if (module.uuid)
40*f6aab3d8Srobert     json_module["uuid"] = *module.uuid;
41*f6aab3d8Srobert   return std::move(json_module);
42*f6aab3d8Srobert }
43*f6aab3d8Srobert 
fromJSON(const json::Value & value,JSONModule & module,Path path)44*f6aab3d8Srobert bool fromJSON(const json::Value &value, JSONModule &module, Path path) {
45*f6aab3d8Srobert   ObjectMapper o(value, path);
46*f6aab3d8Srobert   return o && o.map("systemPath", module.system_path) &&
47*f6aab3d8Srobert          o.map("file", module.file) &&
48*f6aab3d8Srobert          o.map("loadAddress", module.load_address) &&
49*f6aab3d8Srobert          o.map("uuid", module.uuid);
50*f6aab3d8Srobert }
51*f6aab3d8Srobert 
toJSON(const JSONThread & thread)52*f6aab3d8Srobert json::Value toJSON(const JSONThread &thread) {
53*f6aab3d8Srobert   json::Object obj{{"tid", thread.tid}};
54*f6aab3d8Srobert   if (thread.ipt_trace)
55*f6aab3d8Srobert     obj["iptTrace"] = *thread.ipt_trace;
56*f6aab3d8Srobert   return obj;
57*f6aab3d8Srobert }
58*f6aab3d8Srobert 
fromJSON(const json::Value & value,JSONThread & thread,Path path)59*f6aab3d8Srobert bool fromJSON(const json::Value &value, JSONThread &thread, Path path) {
60*f6aab3d8Srobert   ObjectMapper o(value, path);
61*f6aab3d8Srobert   return o && o.map("tid", thread.tid) && o.map("iptTrace", thread.ipt_trace);
62*f6aab3d8Srobert }
63*f6aab3d8Srobert 
toJSON(const JSONProcess & process)64*f6aab3d8Srobert json::Value toJSON(const JSONProcess &process) {
65*f6aab3d8Srobert   return Object{
66*f6aab3d8Srobert       {"pid", process.pid},
67*f6aab3d8Srobert       {"triple", process.triple},
68*f6aab3d8Srobert       {"threads", process.threads},
69*f6aab3d8Srobert       {"modules", process.modules},
70*f6aab3d8Srobert   };
71*f6aab3d8Srobert }
72*f6aab3d8Srobert 
fromJSON(const json::Value & value,JSONProcess & process,Path path)73*f6aab3d8Srobert bool fromJSON(const json::Value &value, JSONProcess &process, Path path) {
74*f6aab3d8Srobert   ObjectMapper o(value, path);
75*f6aab3d8Srobert   return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
76*f6aab3d8Srobert          o.map("threads", process.threads) && o.map("modules", process.modules);
77*f6aab3d8Srobert }
78*f6aab3d8Srobert 
toJSON(const JSONCpu & cpu)79*f6aab3d8Srobert json::Value toJSON(const JSONCpu &cpu) {
80*f6aab3d8Srobert   return Object{
81*f6aab3d8Srobert       {"id", cpu.id},
82*f6aab3d8Srobert       {"iptTrace", cpu.ipt_trace},
83*f6aab3d8Srobert       {"contextSwitchTrace", cpu.context_switch_trace},
84*f6aab3d8Srobert   };
85*f6aab3d8Srobert }
86*f6aab3d8Srobert 
fromJSON(const json::Value & value,JSONCpu & cpu,Path path)87*f6aab3d8Srobert bool fromJSON(const json::Value &value, JSONCpu &cpu, Path path) {
88*f6aab3d8Srobert   ObjectMapper o(value, path);
89*f6aab3d8Srobert   uint64_t cpu_id;
90*f6aab3d8Srobert   if (!(o && o.map("id", cpu_id) && o.map("iptTrace", cpu.ipt_trace) &&
91*f6aab3d8Srobert         o.map("contextSwitchTrace", cpu.context_switch_trace)))
92*f6aab3d8Srobert     return false;
93*f6aab3d8Srobert   cpu.id = cpu_id;
94*f6aab3d8Srobert   return true;
95*f6aab3d8Srobert }
96*f6aab3d8Srobert 
toJSON(const pt_cpu & cpu_info)97*f6aab3d8Srobert json::Value toJSON(const pt_cpu &cpu_info) {
98*f6aab3d8Srobert   return Object{
99*f6aab3d8Srobert       {"vendor", cpu_info.vendor == pcv_intel ? "GenuineIntel" : "Unknown"},
100*f6aab3d8Srobert       {"family", cpu_info.family},
101*f6aab3d8Srobert       {"model", cpu_info.model},
102*f6aab3d8Srobert       {"stepping", cpu_info.stepping},
103*f6aab3d8Srobert   };
104*f6aab3d8Srobert }
105*f6aab3d8Srobert 
fromJSON(const json::Value & value,pt_cpu & cpu_info,Path path)106*f6aab3d8Srobert bool fromJSON(const json::Value &value, pt_cpu &cpu_info, Path path) {
107*f6aab3d8Srobert   ObjectMapper o(value, path);
108*f6aab3d8Srobert   std::string vendor;
109*f6aab3d8Srobert   uint64_t family, model, stepping;
110*f6aab3d8Srobert   if (!(o && o.map("vendor", vendor) && o.map("family", family) &&
111*f6aab3d8Srobert         o.map("model", model) && o.map("stepping", stepping)))
112*f6aab3d8Srobert     return false;
113*f6aab3d8Srobert   cpu_info.vendor = vendor == "GenuineIntel" ? pcv_intel : pcv_unknown;
114*f6aab3d8Srobert   cpu_info.family = family;
115*f6aab3d8Srobert   cpu_info.model = model;
116*f6aab3d8Srobert   cpu_info.stepping = stepping;
117*f6aab3d8Srobert   return true;
118*f6aab3d8Srobert }
119*f6aab3d8Srobert 
toJSON(const JSONKernel & kernel)120*f6aab3d8Srobert json::Value toJSON(const JSONKernel &kernel) {
121*f6aab3d8Srobert   json::Object json_module;
122*f6aab3d8Srobert   if (kernel.load_address)
123*f6aab3d8Srobert     json_module["loadAddress"] = toJSON(*kernel.load_address, true);
124*f6aab3d8Srobert   json_module["file"] = kernel.file;
125*f6aab3d8Srobert   return std::move(json_module);
126*f6aab3d8Srobert }
127*f6aab3d8Srobert 
fromJSON(const json::Value & value,JSONKernel & kernel,Path path)128*f6aab3d8Srobert bool fromJSON(const json::Value &value, JSONKernel &kernel, Path path) {
129*f6aab3d8Srobert   ObjectMapper o(value, path);
130*f6aab3d8Srobert   return o && o.map("loadAddress", kernel.load_address) &&
131*f6aab3d8Srobert          o.map("file", kernel.file);
132*f6aab3d8Srobert }
133*f6aab3d8Srobert 
toJSON(const JSONTraceBundleDescription & bundle_description)134*f6aab3d8Srobert json::Value toJSON(const JSONTraceBundleDescription &bundle_description) {
135*f6aab3d8Srobert   return Object{
136*f6aab3d8Srobert       {"type", bundle_description.type},
137*f6aab3d8Srobert       {"processes", bundle_description.processes},
138*f6aab3d8Srobert       // We have to do this because the compiler fails at doing it
139*f6aab3d8Srobert       // automatically because pt_cpu is not in a namespace
140*f6aab3d8Srobert       {"cpuInfo", toJSON(bundle_description.cpu_info)},
141*f6aab3d8Srobert       {"cpus", bundle_description.cpus},
142*f6aab3d8Srobert       {"tscPerfZeroConversion", bundle_description.tsc_perf_zero_conversion},
143*f6aab3d8Srobert       {"kernel", bundle_description.kernel}};
144*f6aab3d8Srobert }
145*f6aab3d8Srobert 
fromJSON(const json::Value & value,JSONTraceBundleDescription & bundle_description,Path path)146*f6aab3d8Srobert bool fromJSON(const json::Value &value,
147*f6aab3d8Srobert               JSONTraceBundleDescription &bundle_description, Path path) {
148*f6aab3d8Srobert   ObjectMapper o(value, path);
149*f6aab3d8Srobert   if (!(o && o.map("processes", bundle_description.processes) &&
150*f6aab3d8Srobert         o.map("type", bundle_description.type) &&
151*f6aab3d8Srobert         o.map("cpus", bundle_description.cpus) &&
152*f6aab3d8Srobert         o.map("tscPerfZeroConversion",
153*f6aab3d8Srobert               bundle_description.tsc_perf_zero_conversion) &&
154*f6aab3d8Srobert         o.map("kernel", bundle_description.kernel)))
155*f6aab3d8Srobert     return false;
156*f6aab3d8Srobert   if (bundle_description.cpus && !bundle_description.tsc_perf_zero_conversion) {
157*f6aab3d8Srobert     path.report(
158*f6aab3d8Srobert         "\"tscPerfZeroConversion\" is required when \"cpus\" is provided");
159*f6aab3d8Srobert     return false;
160*f6aab3d8Srobert   }
161*f6aab3d8Srobert   // We have to do this because the compiler fails at doing it automatically
162*f6aab3d8Srobert   // because pt_cpu is not in a namespace
163*f6aab3d8Srobert   if (!fromJSON(*value.getAsObject()->get("cpuInfo"),
164*f6aab3d8Srobert                 bundle_description.cpu_info, path.field("cpuInfo")))
165*f6aab3d8Srobert     return false;
166*f6aab3d8Srobert 
167*f6aab3d8Srobert   // When kernel section is present, this is kernel-only tracing. Thus, throw an
168*f6aab3d8Srobert   // error if the "processes" section is non-empty or the "cpus" section is not
169*f6aab3d8Srobert   // present.
170*f6aab3d8Srobert   if (bundle_description.kernel) {
171*f6aab3d8Srobert     if (bundle_description.processes &&
172*f6aab3d8Srobert         !bundle_description.processes->empty()) {
173*f6aab3d8Srobert       path.report("\"processes\" must be empty when \"kernel\" is provided");
174*f6aab3d8Srobert       return false;
175*f6aab3d8Srobert     }
176*f6aab3d8Srobert     if (!bundle_description.cpus) {
177*f6aab3d8Srobert       path.report("\"cpus\" is required when \"kernel\" is provided");
178*f6aab3d8Srobert       return false;
179*f6aab3d8Srobert     }
180*f6aab3d8Srobert   } else if (!bundle_description.processes) {
181*f6aab3d8Srobert     // Usermode tracing requires processes section.
182*f6aab3d8Srobert     path.report("\"processes\" is required when \"kernel\" is not provided");
183*f6aab3d8Srobert     return false;
184*f6aab3d8Srobert   }
185*f6aab3d8Srobert   return true;
186*f6aab3d8Srobert }
187*f6aab3d8Srobert 
188*f6aab3d8Srobert } // namespace trace_intel_pt
189*f6aab3d8Srobert } // namespace lldb_private
190