blob: a8ce2c0e5588f143ccf096c102f531e97ee0d470 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SRC_TRACE_PROCESSOR_SQLITE_FUNCTIONS_UTILS_H_
#define SRC_TRACE_PROCESSOR_SQLITE_FUNCTIONS_UTILS_H_
#include <sqlite3.h>
#include <unordered_map>
#include "perfetto/ext/base/base64.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/trace_processor/demangle.h"
#include "protos/perfetto/common/builtin_clock.pbzero.h"
#include "src/trace_processor/export_json.h"
#include "src/trace_processor/importers/common/clock_tracker.h"
#include "src/trace_processor/sqlite/functions/create_function_internal.h"
#include "src/trace_processor/util/status_macros.h"
#include "src/trace_processor/sqlite/functions/register_function.h"
namespace perfetto {
namespace trace_processor {
struct ExportJson : public SqlFunction {
using Context = TraceStorage;
static base::Status Run(TraceStorage* storage,
size_t /*argc*/,
sqlite3_value** argv,
SqlValue& /*out*/,
Destructors&);
};
base::Status ExportJson::Run(TraceStorage* storage,
size_t /*argc*/,
sqlite3_value** argv,
SqlValue& /*out*/,
Destructors&) {
base::ScopedFstream output;
if (sqlite3_value_type(argv[0]) == SQLITE_INTEGER) {
// Assume input is an FD.
output.reset(fdopen(sqlite3_value_int(argv[0]), "w"));
if (!output) {
return base::ErrStatus(
"EXPORT_JSON: Couldn't open output file from given FD");
}
} else {
const char* filename =
reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
output = base::OpenFstream(filename, "w");
if (!output) {
return base::ErrStatus("EXPORT_JSON: Couldn't open output file");
}
}
return json::ExportJson(storage, output.get());
}
struct Hash : public SqlFunction {
static base::Status Run(void*,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors&);
};
base::Status Hash::Run(void*,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors&) {
base::Hasher hash;
for (size_t i = 0; i < argc; ++i) {
sqlite3_value* value = argv[i];
int type = sqlite3_value_type(value);
switch (type) {
case SQLITE_INTEGER:
hash.Update(sqlite3_value_int64(value));
break;
case SQLITE_TEXT: {
const char* ptr =
reinterpret_cast<const char*>(sqlite3_value_text(value));
hash.Update(ptr, strlen(ptr));
break;
}
default:
return base::ErrStatus("HASH: arg %zu has unknown type %d", i, type);
}
}
out = SqlValue::Long(static_cast<int64_t>(hash.digest()));
return base::OkStatus();
}
struct Base64Encode : public SqlFunction {
static base::Status Run(void*,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors&);
};
base::Status Base64Encode::Run(void*,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors) {
if (argc != 1)
return base::ErrStatus("Unsupported number of arg passed to Base64Encode");
sqlite3_value* value = argv[0];
if (sqlite3_value_type(value) != SQLITE_BLOB)
return base::ErrStatus("Base64Encode only supports bytes argument");
size_t byte_count = static_cast<size_t>(sqlite3_value_bytes(value));
std::string res = base::Base64Encode(sqlite3_value_blob(value), byte_count);
std::unique_ptr<char, base::FreeDeleter> s(
static_cast<char*>(malloc(res.size() + 1)));
memcpy(s.get(), res.c_str(), res.size() + 1);
out = SqlValue::String(s.release());
destructors.string_destructor = free;
return base::OkStatus();
}
struct Demangle : public SqlFunction {
static base::Status Run(void*,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors);
};
base::Status Demangle::Run(void*,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors) {
if (argc != 1)
return base::ErrStatus("Unsupported number of arg passed to DEMANGLE");
sqlite3_value* value = argv[0];
if (sqlite3_value_type(value) == SQLITE_NULL)
return base::OkStatus();
if (sqlite3_value_type(value) != SQLITE_TEXT)
return base::ErrStatus("Unsupported type of arg passed to DEMANGLE");
const char* mangled =
reinterpret_cast<const char*>(sqlite3_value_text(value));
std::unique_ptr<char, base::FreeDeleter> demangled =
demangle::Demangle(mangled);
if (!demangled)
return base::OkStatus();
destructors.string_destructor = free;
out = SqlValue::String(demangled.release());
return base::OkStatus();
}
struct WriteFile : public SqlFunction {
using Context = TraceStorage;
static base::Status Run(TraceStorage* storage,
size_t,
sqlite3_value** argv,
SqlValue&,
Destructors&);
};
base::Status WriteFile::Run(TraceStorage*,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors&) {
if (argc != 2) {
return base::ErrStatus("WRITE_FILE: expected %d args but got %zu", 2, argc);
}
base::Status status =
sqlite_utils::TypeCheckSqliteValue(argv[0], SqlValue::kString);
if (!status.ok()) {
return base::ErrStatus("WRITE_FILE: argument 1, filename; %s",
status.c_message());
}
status = sqlite_utils::TypeCheckSqliteValue(argv[1], SqlValue::kBytes);
if (!status.ok()) {
return base::ErrStatus("WRITE_FILE: argument 2, content; %s",
status.c_message());
}
const std::string filename =
reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
base::ScopedFstream file = base::OpenFstream(filename.c_str(), "wb");
if (!file) {
return base::ErrStatus("WRITE_FILE: Couldn't open output file %s (%s)",
filename.c_str(), strerror(errno));
}
int int_len = sqlite3_value_bytes(argv[1]);
PERFETTO_CHECK(int_len >= 0);
size_t len = (static_cast<size_t>(int_len));
// Make sure to call last as sqlite3_value_bytes can invalidate pointer
// returned.
const void* data = sqlite3_value_text(argv[1]);
if (fwrite(data, 1, len, file.get()) != len || fflush(file.get()) != 0) {
return base::ErrStatus("WRITE_FILE: Failed to write to file %s (%s)",
filename.c_str(), strerror(errno));
}
out = SqlValue::Long(int_len);
return util::OkStatus();
}
struct ExtractArg : public SqlFunction {
using Context = TraceStorage;
static base::Status Run(TraceStorage* storage,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors);
};
base::Status ExtractArg::Run(TraceStorage* storage,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors) {
if (argc != 2)
return base::ErrStatus("EXTRACT_ARG: 2 args required");
// If the arg set id is null, just return null as the result.
if (sqlite3_value_type(argv[0]) == SQLITE_NULL)
return base::OkStatus();
if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER)
return base::ErrStatus("EXTRACT_ARG: 1st argument should be arg set id");
if (sqlite3_value_type(argv[1]) != SQLITE_TEXT)
return base::ErrStatus("EXTRACT_ARG: 2nd argument should be key");
uint32_t arg_set_id = static_cast<uint32_t>(sqlite3_value_int(argv[0]));
const char* key = reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
base::Optional<Variadic> opt_value;
RETURN_IF_ERROR(storage->ExtractArg(arg_set_id, key, &opt_value));
if (!opt_value)
return base::OkStatus();
// This function always returns static strings (i.e. scoped to lifetime
// of the TraceStorage thread pool) so prevent SQLite from making copies.
destructors.string_destructor = sqlite_utils::kSqliteStatic;
switch (opt_value->type) {
case Variadic::kNull:
return base::OkStatus();
case Variadic::kInt:
out = SqlValue::Long(opt_value->int_value);
return base::OkStatus();
case Variadic::kUint:
out = SqlValue::Long(static_cast<int64_t>(opt_value->uint_value));
return base::OkStatus();
case Variadic::kString:
out =
SqlValue::String(storage->GetString(opt_value->string_value).data());
return base::OkStatus();
case Variadic::kReal:
out = SqlValue::Double(opt_value->real_value);
return base::OkStatus();
case Variadic::kBool:
out = SqlValue::Long(opt_value->bool_value);
return base::OkStatus();
case Variadic::kPointer:
out = SqlValue::Long(static_cast<int64_t>(opt_value->pointer_value));
return base::OkStatus();
case Variadic::kJson:
out = SqlValue::String(storage->GetString(opt_value->json_value).data());
return base::OkStatus();
}
PERFETTO_FATAL("For GCC");
}
struct AbsTimeStr : public SqlFunction {
using Context = ClockTracker;
static base::Status Run(ClockTracker* tracker,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors);
};
base::Status AbsTimeStr::Run(ClockTracker* tracker,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors) {
if (argc != 1) {
return base::ErrStatus("ABS_TIME_STR: 1 arg required");
}
// If the timestamp is null, just return null as the result.
if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
return base::OkStatus();
}
if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
return base::ErrStatus("ABS_TIME_STR: first argument should be timestamp");
}
int64_t ts = sqlite3_value_int64(argv[0]);
base::Optional<std::string> iso8601 = tracker->FromTraceTimeAsISO8601(ts);
if (!iso8601.has_value()) {
return base::OkStatus();
}
std::unique_ptr<char, base::FreeDeleter> s(
static_cast<char*>(malloc(iso8601->size() + 1)));
memcpy(s.get(), iso8601->c_str(), iso8601->size() + 1);
destructors.string_destructor = free;
out = SqlValue::String(s.release());
return base::OkStatus();
}
struct ToMonotonic : public SqlFunction {
using Context = ClockTracker;
static base::Status Run(ClockTracker* tracker,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors& destructors);
};
base::Status ToMonotonic::Run(ClockTracker* tracker,
size_t argc,
sqlite3_value** argv,
SqlValue& out,
Destructors&) {
if (argc != 1) {
return base::ErrStatus("TO_MONOTONIC: 1 arg required");
}
// If the timestamp is null, just return null as the result.
if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
return base::OkStatus();
}
if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
return base::ErrStatus("TO_MONOTONIC: first argument should be timestamp");
}
int64_t ts = sqlite3_value_int64(argv[0]);
base::Optional<int64_t> monotonic =
tracker->FromTraceTime(protos::pbzero::BUILTIN_CLOCK_MONOTONIC, ts);
if (!monotonic.has_value()) {
// This means we'll return NULL
return base::OkStatus();
}
out = SqlValue::Long(*monotonic);
return base::OkStatus();
}
struct SourceGeq : public SqlFunction {
static base::Status Run(void*,
size_t,
sqlite3_value**,
SqlValue&,
Destructors&) {
return base::ErrStatus(
"SOURCE_GEQ should not be called from the global scope");
}
};
struct Glob : public SqlFunction {
static base::Status Run(void*,
size_t,
sqlite3_value** argv,
SqlValue& out,
Destructors&) {
const char* pattern =
reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
const char* text =
reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
if (pattern && text) {
out = SqlValue::Long(sqlite3_strglob(pattern, text) == 0);
}
return base::OkStatus();
}
};
} // namespace trace_processor
} // namespace perfetto
#endif // SRC_TRACE_PROCESSOR_SQLITE_FUNCTIONS_UTILS_H_