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