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(size_expression: 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, ipt_trace_size: m_options.m_ipt_trace_size, |
80 | enable_tsc: m_options.m_enable_tsc, psb_period: m_options.m_psb_period)) |
81 | result.SetError(error: 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(size_expression: 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(size_expression: 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(Radix: 0, Result&: 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 | void CommandObjectProcessTraceStartIntelPT::DoExecute( |
162 | Args &command, CommandReturnObject &result) { |
163 | if (Error err = m_trace.Start( |
164 | ipt_trace_size: m_options.m_ipt_trace_size, total_buffer_size_limit: m_options.m_process_buffer_size_limit, |
165 | enable_tsc: m_options.m_enable_tsc, psb_period: m_options.m_psb_period, |
166 | m_per_cpu_tracing: m_options.m_per_cpu_tracing, disable_cgroup_filtering: m_options.m_disable_cgroup_filtering)) |
167 | result.SetError(error: Status(std::move(err))); |
168 | else |
169 | result.SetStatus(eReturnStatusSuccessFinishResult); |
170 | } |
171 | |
172 | std::optional<uint64_t> |
173 | ParsingUtils::ParseUserFriendlySizeExpression(llvm::StringRef size_expression) { |
174 | if (size_expression.empty()) { |
175 | return std::nullopt; |
176 | } |
177 | const uint64_t kBytesMultiplier = 1; |
178 | const uint64_t kKibiBytesMultiplier = 1024; |
179 | const uint64_t kMebiBytesMultiplier = 1024 * 1024; |
180 | |
181 | DenseMap<StringRef, uint64_t> multipliers = { |
182 | {"mib" , kMebiBytesMultiplier}, {"mb" , kMebiBytesMultiplier}, |
183 | {"m" , kMebiBytesMultiplier}, {"kib" , kKibiBytesMultiplier}, |
184 | {"kb" , kKibiBytesMultiplier}, {"k" , kKibiBytesMultiplier}, |
185 | {"b" , kBytesMultiplier}, {"" , kBytesMultiplier}}; |
186 | |
187 | const auto non_digit_index = size_expression.find_first_not_of(Chars: "0123456789" ); |
188 | if (non_digit_index == 0) { // expression starts from from non-digit char. |
189 | return std::nullopt; |
190 | } |
191 | |
192 | const llvm::StringRef number_part = |
193 | non_digit_index == llvm::StringRef::npos |
194 | ? size_expression |
195 | : size_expression.substr(Start: 0, N: non_digit_index); |
196 | uint64_t parsed_number; |
197 | if (number_part.getAsInteger(Radix: 10, Result&: parsed_number)) { |
198 | return std::nullopt; |
199 | } |
200 | |
201 | if (non_digit_index != llvm::StringRef::npos) { // if expression has units. |
202 | const auto multiplier = size_expression.substr(Start: non_digit_index).lower(); |
203 | |
204 | auto it = multipliers.find(Val: multiplier); |
205 | if (it == multipliers.end()) |
206 | return std::nullopt; |
207 | |
208 | return parsed_number * it->second; |
209 | } else { |
210 | return parsed_number; |
211 | } |
212 | } |
213 | |