1 //===-- CommandObjectTraceStartIntelPT.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 "CommandObjectTraceStartIntelPT.h"
10 #include "TraceIntelPT.h"
11 #include "TraceIntelPTConstants.h"
12 #include "lldb/Host/OptionParser.h"
13 #include "lldb/Interpreter/CommandOptionArgumentTable.h"
14 #include "lldb/Target/Process.h"
15 #include "lldb/Target/Trace.h"
16 #include <optional>
17 
18 using namespace lldb;
19 using namespace lldb_private;
20 using namespace lldb_private::trace_intel_pt;
21 using namespace llvm;
22 
23 // CommandObjectThreadTraceStartIntelPT
24 
25 #define LLDB_OPTIONS_thread_trace_start_intel_pt
26 #include "TraceIntelPTCommandOptions.inc"
27 
28 Status CommandObjectThreadTraceStartIntelPT::CommandOptions::SetOptionValue(
29     uint32_t option_idx, llvm::StringRef option_arg,
30     ExecutionContext *execution_context) {
31   Status error;
32   const int short_option = m_getopt_table[option_idx].val;
33 
34   switch (short_option) {
35   case 's': {
36     if (std::optional<uint64_t> bytes =
37             ParsingUtils::ParseUserFriendlySizeExpression(option_arg))
38       m_ipt_trace_size = *bytes;
39     else
40       error.SetErrorStringWithFormat("invalid bytes expression for '%s'",
41                                      option_arg.str().c_str());
42     break;
43   }
44   case 't': {
45     m_enable_tsc = true;
46     break;
47   }
48   case 'p': {
49     int64_t psb_period;
50     if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) ||
51         psb_period < 0)
52       error.SetErrorStringWithFormat("invalid integer value for option '%s'",
53                                      option_arg.str().c_str());
54     else
55       m_psb_period = psb_period;
56     break;
57   }
58   default:
59     llvm_unreachable("Unimplemented option");
60   }
61   return error;
62 }
63 
64 void CommandObjectThreadTraceStartIntelPT::CommandOptions::
65     OptionParsingStarting(ExecutionContext *execution_context) {
66   m_ipt_trace_size = kDefaultIptTraceSize;
67   m_enable_tsc = kDefaultEnableTscValue;
68   m_psb_period = kDefaultPsbPeriod;
69 }
70 
71 llvm::ArrayRef<OptionDefinition>
72 CommandObjectThreadTraceStartIntelPT::CommandOptions::GetDefinitions() {
73   return llvm::ArrayRef(g_thread_trace_start_intel_pt_options);
74 }
75 
76 bool CommandObjectThreadTraceStartIntelPT::DoExecuteOnThreads(
77     Args &command, CommandReturnObject &result,
78     llvm::ArrayRef<lldb::tid_t> tids) {
79   if (Error err = m_trace.Start(tids, m_options.m_ipt_trace_size,
80                                 m_options.m_enable_tsc, m_options.m_psb_period))
81     result.SetError(Status(std::move(err)));
82   else
83     result.SetStatus(eReturnStatusSuccessFinishResult);
84 
85   return result.Succeeded();
86 }
87 
88 /// CommandObjectProcessTraceStartIntelPT
89 
90 #define LLDB_OPTIONS_process_trace_start_intel_pt
91 #include "TraceIntelPTCommandOptions.inc"
92 
93 Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue(
94     uint32_t option_idx, llvm::StringRef option_arg,
95     ExecutionContext *execution_context) {
96   Status error;
97   const int short_option = m_getopt_table[option_idx].val;
98 
99   switch (short_option) {
100   case 's': {
101     if (std::optional<uint64_t> bytes =
102             ParsingUtils::ParseUserFriendlySizeExpression(option_arg))
103       m_ipt_trace_size = *bytes;
104     else
105       error.SetErrorStringWithFormat("invalid bytes expression for '%s'",
106                                      option_arg.str().c_str());
107     break;
108   }
109   case 'l': {
110     if (std::optional<uint64_t> bytes =
111             ParsingUtils::ParseUserFriendlySizeExpression(option_arg))
112       m_process_buffer_size_limit = *bytes;
113     else
114       error.SetErrorStringWithFormat("invalid bytes expression for '%s'",
115                                      option_arg.str().c_str());
116     break;
117   }
118   case 't': {
119     m_enable_tsc = true;
120     break;
121   }
122   case 'c': {
123     m_per_cpu_tracing = true;
124     break;
125   }
126   case 'd': {
127     m_disable_cgroup_filtering = true;
128     break;
129   }
130   case 'p': {
131     int64_t psb_period;
132     if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) ||
133         psb_period < 0)
134       error.SetErrorStringWithFormat("invalid integer value for option '%s'",
135                                      option_arg.str().c_str());
136     else
137       m_psb_period = psb_period;
138     break;
139   }
140   default:
141     llvm_unreachable("Unimplemented option");
142   }
143   return error;
144 }
145 
146 void CommandObjectProcessTraceStartIntelPT::CommandOptions::
147     OptionParsingStarting(ExecutionContext *execution_context) {
148   m_ipt_trace_size = kDefaultIptTraceSize;
149   m_process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
150   m_enable_tsc = kDefaultEnableTscValue;
151   m_psb_period = kDefaultPsbPeriod;
152   m_per_cpu_tracing = kDefaultPerCpuTracing;
153   m_disable_cgroup_filtering = kDefaultDisableCgroupFiltering;
154 }
155 
156 llvm::ArrayRef<OptionDefinition>
157 CommandObjectProcessTraceStartIntelPT::CommandOptions::GetDefinitions() {
158   return llvm::ArrayRef(g_process_trace_start_intel_pt_options);
159 }
160 
161 bool CommandObjectProcessTraceStartIntelPT::DoExecute(
162     Args &command, CommandReturnObject &result) {
163   if (Error err = m_trace.Start(
164           m_options.m_ipt_trace_size, m_options.m_process_buffer_size_limit,
165           m_options.m_enable_tsc, m_options.m_psb_period,
166           m_options.m_per_cpu_tracing, m_options.m_disable_cgroup_filtering))
167     result.SetError(Status(std::move(err)));
168   else
169     result.SetStatus(eReturnStatusSuccessFinishResult);
170 
171   return result.Succeeded();
172 }
173 
174 std::optional<uint64_t>
175 ParsingUtils::ParseUserFriendlySizeExpression(llvm::StringRef size_expression) {
176   if (size_expression.empty()) {
177     return std::nullopt;
178   }
179   const uint64_t kBytesMultiplier = 1;
180   const uint64_t kKibiBytesMultiplier = 1024;
181   const uint64_t kMebiBytesMultiplier = 1024 * 1024;
182 
183   DenseMap<StringRef, uint64_t> multipliers = {
184       {"mib", kMebiBytesMultiplier}, {"mb", kMebiBytesMultiplier},
185       {"m", kMebiBytesMultiplier},   {"kib", kKibiBytesMultiplier},
186       {"kb", kKibiBytesMultiplier},  {"k", kKibiBytesMultiplier},
187       {"b", kBytesMultiplier},       {"", kBytesMultiplier}};
188 
189   const auto non_digit_index = size_expression.find_first_not_of("0123456789");
190   if (non_digit_index == 0) { // expression starts from from non-digit char.
191     return std::nullopt;
192   }
193 
194   const llvm::StringRef number_part =
195       non_digit_index == llvm::StringRef::npos
196           ? size_expression
197           : size_expression.substr(0, non_digit_index);
198   uint64_t parsed_number;
199   if (number_part.getAsInteger(10, parsed_number)) {
200     return std::nullopt;
201   }
202 
203   if (non_digit_index != llvm::StringRef::npos) { // if expression has units.
204     const auto multiplier = size_expression.substr(non_digit_index).lower();
205 
206     auto it = multipliers.find(multiplier);
207     if (it == multipliers.end())
208       return std::nullopt;
209 
210     return parsed_number * it->second;
211   } else {
212     return parsed_number;
213   }
214 }
215