| 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 = Status::FromErrorStringWithFormat( |
| 41 | format: "invalid bytes expression for '%s'" , 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 = Status::FromErrorStringWithFormat( |
| 53 | format: "invalid integer value for option '%s'" , 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(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 = Status::FromErrorStringWithFormat( |
| 106 | format: "invalid bytes expression for '%s'" , 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 = Status::FromErrorStringWithFormat( |
| 115 | format: "invalid bytes expression for '%s'" , 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 = Status::FromErrorStringWithFormat( |
| 135 | format: "invalid integer value for option '%s'" , 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(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 | |