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
14using namespace lldb;
15using namespace lldb_private;
16using namespace lldb_private::trace_intel_pt;
17using namespace llvm;
18using namespace llvm::json;
19
20namespace lldb_private {
21namespace trace_intel_pt {
22
23std::optional<std::vector<lldb::cpu_id_t>>
24JSONTraceBundleDescription::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(cpu.id);
30  return cpu_ids;
31}
32
33json::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(module.load_address, true);
39  if (module.uuid)
40    json_module["uuid"] = *module.uuid;
41  return std::move(json_module);
42}
43
44bool fromJSON(const json::Value &value, JSONModule &module, Path path) {
45  ObjectMapper o(value, path);
46  return o && o.map("systemPath", module.system_path) &&
47         o.map("file", module.file) &&
48         o.map("loadAddress", module.load_address) &&
49         o.map("uuid", module.uuid);
50}
51
52json::Value toJSON(const JSONThread &thread) {
53  json::Object obj{{"tid", thread.tid}};
54  if (thread.ipt_trace)
55    obj["iptTrace"] = *thread.ipt_trace;
56  return obj;
57}
58
59bool fromJSON(const json::Value &value, JSONThread &thread, Path path) {
60  ObjectMapper o(value, path);
61  return o && o.map("tid", thread.tid) && o.map("iptTrace", thread.ipt_trace);
62}
63
64json::Value toJSON(const JSONProcess &process) {
65  return Object{
66      {"pid", process.pid},
67      {"triple", process.triple},
68      {"threads", process.threads},
69      {"modules", process.modules},
70  };
71}
72
73bool fromJSON(const json::Value &value, JSONProcess &process, Path path) {
74  ObjectMapper o(value, path);
75  return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
76         o.map("threads", process.threads) && o.map("modules", process.modules);
77}
78
79json::Value toJSON(const JSONCpu &cpu) {
80  return Object{
81      {"id", cpu.id},
82      {"iptTrace", cpu.ipt_trace},
83      {"contextSwitchTrace", cpu.context_switch_trace},
84  };
85}
86
87bool fromJSON(const json::Value &value, JSONCpu &cpu, Path path) {
88  ObjectMapper o(value, path);
89  uint64_t cpu_id;
90  if (!(o && o.map("id", cpu_id) && o.map("iptTrace", cpu.ipt_trace) &&
91        o.map("contextSwitchTrace", cpu.context_switch_trace)))
92    return false;
93  cpu.id = cpu_id;
94  return true;
95}
96
97json::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
106bool 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("vendor", vendor) && o.map("family", family) &&
111        o.map("model", model) && o.map("stepping", 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
120json::Value toJSON(const JSONKernel &kernel) {
121  json::Object json_module;
122  if (kernel.load_address)
123    json_module["loadAddress"] = toJSON(*kernel.load_address, true);
124  json_module["file"] = kernel.file;
125  return std::move(json_module);
126}
127
128bool fromJSON(const json::Value &value, JSONKernel &kernel, Path path) {
129  ObjectMapper o(value, path);
130  return o && o.map("loadAddress", kernel.load_address) &&
131         o.map("file", kernel.file);
132}
133
134json::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
146bool fromJSON(const json::Value &value,
147              JSONTraceBundleDescription &bundle_description, Path path) {
148  ObjectMapper o(value, path);
149  if (!(o && o.map("processes", bundle_description.processes) &&
150        o.map("type", bundle_description.type) &&
151        o.map("cpus", bundle_description.cpus) &&
152        o.map("tscPerfZeroConversion",
153              bundle_description.tsc_perf_zero_conversion) &&
154        o.map("kernel", bundle_description.kernel)))
155    return false;
156  if (bundle_description.cpus && !bundle_description.tsc_perf_zero_conversion) {
157    path.report(
158        "\"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("cpuInfo"),
164                bundle_description.cpu_info, path.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("\"processes\" must be empty when \"kernel\" is provided");
174      return false;
175    }
176    if (!bundle_description.cpus) {
177      path.report("\"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("\"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