1 /*
2 * Copyright (C) 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/perfetto_sql/intrinsics/functions/replace_numbers_function.h"
18
19 #include <stdlib.h>
20 #include <cctype>
21 #include <cstdint>
22 #include <cstring>
23 #include <string_view>
24
25 #include "perfetto/base/status.h"
26 #include "perfetto/trace_processor/basic_types.h"
27 #include "protos/perfetto/trace_processor/stack.pbzero.h"
28 #include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
29 #include "src/trace_processor/perfetto_sql/intrinsics/functions/sql_function.h"
30 #include "src/trace_processor/sqlite/sqlite_utils.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32 #include "src/trace_processor/types/trace_processor_context.h"
33 #include "src/trace_processor/util/status_macros.h"
34
35 namespace perfetto {
36 namespace trace_processor {
37 namespace {
38
39 // __intrinsic_strip_hex(name STRING, min_repeated_digits LONG)
40 //
41 // Replaces hexadecimal sequences (with at least one digit) in a string with
42 // "<num>" based on specified criteria.
43 struct StripHexFunction : public SqlFunction {
44 static constexpr char kFunctionName[] = "__intrinsic_strip_hex";
45 using Context = void;
46
Runperfetto::trace_processor::__anon599900070111::StripHexFunction47 static base::Status Run(void* cxt,
48 size_t argc,
49 sqlite3_value** argv,
50 SqlValue& out,
51 Destructors& destructors) {
52 base::Status status = RunImpl(cxt, argc, argv, out, destructors);
53 if (!status.ok()) {
54 return base::ErrStatus("%s: %s", kFunctionName, status.message().c_str());
55 }
56 return status;
57 }
58
StripHexperfetto::trace_processor::__anon599900070111::StripHexFunction59 static std::string StripHex(std::string input, int64_t min_repeated_digits) {
60 std::string result;
61 result.reserve(input.length());
62 for (size_t i = 0; i < input.length();) {
63 bool replace_hex = false;
64 if ((input[i] == 'x' || input[i] == 'X') && i >= 1 &&
65 input[i - 1] == '0') {
66 // Case 1: Special prefixes (0x, 0X) for hex sequence found
67 result += input[i++];
68 // Always try to replace hex after 0x, regardless if they contain digits
69 // or not
70 replace_hex = true;
71 } else if (!isalnum(input[i])) {
72 // Case 2: Non alpha numeric prefix for hex sequence found
73 result += input[i++];
74 } else if (i == 0 && isxdigit(input[i])) {
75 // Case 3: Start of input is hex digit, continue to check hex sequence
76 } else if (isdigit(input[i])) {
77 // Case 4: A digit is found, consider replacing the sequence
78 } else {
79 // Case 5: No potential prefix for hex digits found
80 result += input[i++];
81 continue;
82 }
83
84 size_t hex_start = i;
85 for (; i < input.length() && isxdigit(input[i]); i++) {
86 if (isdigit(input[i])) {
87 replace_hex = true;
88 }
89 }
90 result += replace_hex && (i - hex_start >=
91 static_cast<size_t>(min_repeated_digits))
92 ? "<num>"
93 : input.substr(hex_start, i - hex_start);
94 }
95 return result;
96 }
97
RunImplperfetto::trace_processor::__anon599900070111::StripHexFunction98 static base::Status RunImpl(void*,
99 size_t argc,
100 sqlite3_value** argv,
101 SqlValue& out,
102 Destructors& destructors) {
103 if (argc != 2) {
104 return base::ErrStatus(
105 "%s; Invalid number of arguments: expected 2, actual %zu",
106 kFunctionName, argc);
107 }
108 std::optional<std::string> first_arg = sqlite::utils::SqlValueToString(
109 sqlite::utils::SqliteValueToSqlValue(argv[0]));
110 if (!first_arg.has_value()) {
111 return base::ErrStatus("Invalid name argument for %s expected string",
112 kFunctionName);
113 }
114 const std::string& input = first_arg.value();
115
116 SqlValue second_arg = sqlite::utils::SqliteValueToSqlValue(argv[1]);
117 if (second_arg.type != SqlValue::Type::kLong) {
118 return base::ErrStatus(
119 "Invalid min_repeated_digits argument for %s expected integer",
120 kFunctionName);
121 }
122
123 const int64_t min_repeated_digits = second_arg.AsLong();
124 if (min_repeated_digits < 0) {
125 return base::ErrStatus(
126 "Invalid min_repeated_digits argument for %s expected positive "
127 "integer",
128 kFunctionName);
129 }
130
131 std::string result = StripHex(input, min_repeated_digits);
132 char* result_cstr = static_cast<char*>(malloc(result.length() + 1));
133 memcpy(result_cstr, result.c_str(), result.length() + 1);
134 out = SqlValue::String(result_cstr);
135 destructors.string_destructor = free;
136 return base::OkStatus();
137 }
138 };
139
140 } // namespace
141
RegisterStripHexFunction(PerfettoSqlEngine * engine,TraceProcessorContext * context)142 base::Status RegisterStripHexFunction(PerfettoSqlEngine* engine,
143 TraceProcessorContext* context) {
144 return engine->RegisterStaticFunction<StripHexFunction>(
145 StripHexFunction::kFunctionName, 2, context->storage.get());
146 }
147
SqlStripHex(std::string input,int64_t min_repeated_digits)148 std::string SqlStripHex(std::string input, int64_t min_repeated_digits) {
149 return StripHexFunction::StripHex(input, min_repeated_digits);
150 }
151
152 } // namespace trace_processor
153 } // namespace perfetto
154