Files
rocm-systems/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk-rocpd/sql.cpp
T
Madsen, Jonathan 0e93099fd7 [rocprofv3] SQLite3 database output (rocpd) support + rocprofiler-sdk-rocpd (#403)
* [rocprofv3] rocpd SQLite3 database output support

* Move counters xml and yaml to source/share/rocprofiler-sdk

- more representative of install hierarchy

* Add share/rocprofiler-sdk/rocpd SQL files

* Experimental rocprofiler-sdk SQL API

* rocprofv3 default output format is rocpd

* Fix rocpd event ids for counter collection w/o kernel dispatch

* Remove fktable entries from rocpd_tables.sql

* Fix rocpd schema path

* Fix install component for roctx python bindings

* rocprofiler-sdk-rocpd

- create include/rocprofiler-sdk-rocpd
- create rocprofiler-sdk-rocpd library, package, etc.
- default all "guid" fields to "{{guid}}" in tables
- remove "{{view_uuid}}" support (always unused)

* Migrate rocprofv3 to use rocprofiler-sdk-rocpd

* Fix missing foreign key reference

* Revert change

* Fix cmake comment

* Fix maybe-uninitialized compiler warning

* Fix maybe-uninitialized compiler warning

* Add logging to rocpd_sql_load_schema

* Improve string sanitization when inserting json strings

* Initialize rocpd logging on rocprofiler-sdk-rocpd library load

* Revert lib/output/generatePerfetto.cpp changes

* [temporary] Tweak rocprofv3-test-list-avail-trace-execute test log level

* Update get_install_path for lib/rocprofiler-sdk-rocpd/sql.cpp

- try to resolve issues on RHEL/SLES for dladdr

* Update lib/common/logging.cpp

- enable environ overrides

* dlsym for rocpd_sql_load_schema

* Make dl_info.dli_fname lexically normal

* Implement node_info alternatives if /etc/machine-id does not exist

* Misc include fixes

* SHA256 and UUIDv7 support

* Implement UUIDv7 in generateRocpd.cpp

* Support push/pop environment variables

* Minor tweak

* Fix glog segfaults when unsetting glog env

* Updated CHANGELOG

* Updates tests/pytest-packages

- rocpd_reader.py: RocpdReader

* Update tests / marker_views.sql

- add test_rocpd_data

* Update rocpd_tables.sql

- Use AUTOINCREMENT
- insert "uuid" and "guid" into rocpd_metadata

* Minor updates to generateRocpd.cpp

- don't quote GUID
- use sqlite3_open_v2
- use sqlite3_close_v2

* Update execute_raw_sql_statements_impl

- uses sqlite3_last_insert_rowid for autoincrement

* Update SQL deferred_transaction

- CI check for nullptr to connection

* Apply suggestions from code review

Co-authored-by: Welton, Benjamin <Benjamin.Welton@amd.com>

* Code review updates

- formatting
- replace if with switch
- remove loop for {{uuid}}

* Fix pmc_groups handling in rocprofv3

* Address code review feedback

- Include rocm_version in rocprofv3 version info
- Note `--version` option for `rocprofv3` in CHANGELOG.md
- remove commented out code

* Fix packaging dependencies

* Fix install package step of CI workflow

* Fix install package step of CI workflow

---------

Co-authored-by: Jonathan R. Madsen <jonathanrmadsen@gmail.com>
Co-authored-by: Welton, Benjamin <Benjamin.Welton@amd.com>

[ROCm/rocprofiler-sdk commit: 7afedc63be]
2025-05-30 00:13:19 -05:00

248 righe
8.9 KiB
C++

// MIT License
//
// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#define _GNU_SOURCE 1
#include "lib/common/environment.hpp"
#include "lib/common/filesystem.hpp"
#include "lib/common/logging.hpp"
#include "lib/common/mpl.hpp"
#include "lib/common/string_entry.hpp"
#include "lib/common/utility.hpp"
#include <rocprofiler-sdk-rocpd/rocpd.h>
#include <rocprofiler-sdk-rocpd/sql.h>
#include <rocprofiler-sdk-rocpd/types.h>
#include <rocprofiler-sdk/cxx/details/tokenize.hpp>
#include <fmt/core.h>
#include <fmt/ranges.h>
#include <dlfcn.h>
#include <initializer_list>
#include <unordered_map>
namespace rocpd
{
namespace sql
{
namespace
{
namespace common = ::rocprofiler::common;
namespace fs = ::rocprofiler::common::filesystem;
std::string
get_install_path()
{
auto* _rocpd_sql_load_schema_sym = dlsym(RTLD_DEFAULT, "rocpd_sql_load_schema");
ROCP_CI_LOG_IF(WARNING, !_rocpd_sql_load_schema_sym)
<< "[rocprofiler-sdk-rocpd] dlsym(RTLD_DEFAULT, 'rocpd_sql_load_schema') failed "
"(unexpectedly) from within the rocprofiler-sdk-rocpd library";
if(!_rocpd_sql_load_schema_sym)
_rocpd_sql_load_schema_sym = reinterpret_cast<void*>(&rocpd_sql_load_schema);
if(Dl_info dl_info = {};
dladdr(_rocpd_sql_load_schema_sym, &dl_info) != 0 && dl_info.dli_fname != nullptr)
{
auto _share_path =
fs::path{dl_info.dli_fname}.lexically_normal().parent_path().parent_path() /
std::string{"share/rocprofiler-sdk-rocpd"};
ROCP_INFO << fmt::format("[rocprofiler-sdk-rocpd] resolved rocprofiler-sdk-rocpd SQL "
"schema path as '{}' (dli_fname: {})",
_share_path.string(),
dl_info.dli_fname);
return _share_path;
}
ROCP_CI_LOG(WARNING)
<< "Failed to locate the installation path of rocprofiler-sdk-rocpd via dladdr of the "
"'rocpd_sql_load_schema' symbol (which should be in librocprofiler-sdk-rocpd.so)";
return std::string{};
}
template <typename Tp>
auto
replace_all(std::string val, Tp from, std::string_view to)
{
size_t pos = 0;
while((pos = val.find(from, pos)) != std::string::npos)
{
if constexpr(std::is_same<common::mpl::unqualified_type_t<Tp>, char>::value)
{
val.replace(pos, 1, to);
pos += to.length();
}
else
{
val.replace(pos, std::string_view{from}.length(), to);
pos += to.length();
}
}
return val;
}
} // namespace
} // namespace sql
} // namespace rocpd
extern "C" {
rocpd_status_t
rocpd_sql_load_schema(rocpd_sql_engine_t engine,
rocpd_sql_schema_kind_t kind,
rocpd_sql_options_t options,
const rocpd_sql_schema_jinja_variables_t* variables,
rocpd_sql_load_schema_cb_t callback,
const char** schema_path_hints,
uint64_t num_schema_path_hints,
void* user_data)
{
namespace fs = ::rocpd::sql::fs;
switch(engine)
{
case ROCPD_SQL_ENGINE_SQLITE3:
{
break;
}
case ROCPD_SQL_ENGINE_NONE:
case ROCPD_SQL_ENGINE_LAST:
{
return ROCPD_STATUS_ERROR_SQL_INVALID_ENGINE;
}
}
const auto kind_file_names = std::unordered_map<rocpd_sql_schema_kind_t, std::string_view>{
{ROCPD_SQL_SCHEMA_ROCPD_TABLES, "rocpd_tables.sql"},
{ROCPD_SQL_SCHEMA_ROCPD_INDEXES, "rocpd_indexes.sql"},
{ROCPD_SQL_SCHEMA_ROCPD_VIEWS, "rocpd_views.sql"},
{ROCPD_SQL_SCHEMA_ROCPD_DATA_VIEWS, "data_views.sql"},
{ROCPD_SQL_SCHEMA_ROCPD_SUMMARY_VIEWS, "summary_views.sql"},
{ROCPD_SQL_SCHEMA_ROCPD_MARKER_VIEWS, "marker_views.sql"},
};
const auto _lib_schema_path = rocpd::sql::get_install_path();
const auto _env_schema_path = rocprofiler::common::get_env("ROCPD_SCHEMA_PATH", "");
const auto _usr_schema_path =
(schema_path_hints)
? fmt::format(
"{}",
fmt::join(schema_path_hints, schema_path_hints + num_schema_path_hints, ":"))
: std::string{};
const auto _schema_paths =
fmt::format("{}:{}:{}", _usr_schema_path, _env_schema_path, _lib_schema_path);
if(kind_file_names.count(kind) == 0) return ROCPD_STATUS_ERROR_SQL_INVALID_SCHEMA_KIND;
auto _schema_file = std::optional<std::string>{};
for(const auto& itr : rocprofiler::sdk::parse::tokenize(_schema_paths, ":"))
{
auto _fpath = fs::path{itr} / kind_file_names.at(kind);
ROCP_TRACE << fmt::format("[rocprofiler-sdk-rocpd] Searching for schema file: '{}'",
_fpath.string());
if(fs::exists(_fpath))
{
ROCP_INFO << fmt::format("[rocprofiler-sdk-rocpd] Found schema file: '{}'",
_fpath.string());
_schema_file = _fpath;
break;
}
}
if(!_schema_file) return ROCPD_STATUS_ERROR_SQL_SCHEMA_NOT_FOUND;
auto read_file = [](const std::string& _file_path) -> std::string {
auto _ifs = std::ifstream{_file_path, std::ios::in | std::ios::binary};
if(!_ifs.is_open()) return std::string{};
auto _buffer = std::stringstream{};
_buffer << _ifs.rdbuf();
return _buffer.str();
};
auto _contents = read_file(*_schema_file);
if(_contents.empty()) return ROCPD_STATUS_ERROR_SQL_SCHEMA_PERMISSION_DENIED;
if(engine == ROCPD_SQL_ENGINE_SQLITE3)
{
if((options & ROCPD_SQL_OPTIONS_SQLITE3_PRAGMA_FOREIGN_KEYS) ==
ROCPD_SQL_OPTIONS_SQLITE3_PRAGMA_FOREIGN_KEYS)
_contents = fmt::format("PRAGMA foreign_keys = ON;\n\n{}", _contents);
}
auto _substitutions = std::vector<std::pair<std::string_view, std::string>>{};
using jinja_init_list_t = std::initializer_list<std::pair<std::string_view, const char*>>;
if(variables != nullptr)
{
if(variables->size == 0)
{
return ROCPD_STATUS_ERROR_SQL_INVALID_SCHEMA_KIND;
}
// {{uuid}} is used in table names and require special handling
if(const auto* value = variables->uuid; value != nullptr)
{
auto _value = std::string{value};
// non-empty strings are prefixed with underscore for readability
if(!_value.empty() && _value.find('_') != 0) _value = fmt::format("_{}", _value);
// replace hyphens with underscores since these are used in table/view names
if(_value.find('-') != std::string::npos)
_value = rocpd::sql::replace_all(_value, "-", "_");
// make substitutions
_contents = rocpd::sql::replace_all(_contents, "{{uuid}}", _value);
}
// make substitutions for remaining variables which do not require special handling like
// {{uuid}}
for(auto [key, value] : jinja_init_list_t{{"{{guid}}", variables->guid}})
{
if(value != nullptr)
{
_contents = rocpd::sql::replace_all(_contents, key, std::string_view{value});
}
}
}
const auto* cb_schema_path = _schema_file->c_str();
const auto* cb_schema_contents = _contents.c_str();
if((options & ROCPD_SQL_OPTIONS_SQLITE3_PRAGMA_FOREIGN_KEYS) ==
ROCPD_SQL_OPTIONS_SQLITE3_PRAGMA_FOREIGN_KEYS)
{
cb_schema_path = rocprofiler::common::get_string_entry(cb_schema_path)->c_str();
cb_schema_contents = rocprofiler::common::get_string_entry(cb_schema_contents)->c_str();
}
callback(engine, kind, options, variables, cb_schema_path, cb_schema_contents, user_data);
return ROCPD_STATUS_SUCCESS;
}
}