mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-10 09:32:06 +00:00
Merge pull request #33467 from cmsxbc/addressToLineWithInlines
add function addressToLineWithInlines
This commit is contained in:
commit
496d77c3c3
@ -27,7 +27,7 @@ To analyze the `trace_log` system table:
|
||||
|
||||
For security reasons, introspection functions are disabled by default.
|
||||
|
||||
- Use the `addressToLine`, `addressToSymbol` and `demangle` [introspection functions](../../sql-reference/functions/introspection.md) to get function names and their positions in ClickHouse code. To get a profile for some query, you need to aggregate data from the `trace_log` table. You can aggregate data by individual functions or by the whole stack traces.
|
||||
- Use the `addressToLine`, `addressToLineWithInlines`, `addressToSymbol` and `demangle` [introspection functions](../../sql-reference/functions/introspection.md) to get function names and their positions in ClickHouse code. To get a profile for some query, you need to aggregate data from the `trace_log` table. You can aggregate data by individual functions or by the whole stack traces.
|
||||
|
||||
If you need to visualize `trace_log` info, try [flamegraph](../../interfaces/third-party/gui/#clickhouse-flamegraph) and [speedscope](https://github.com/laplab/clickhouse-speedscope).
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Contains stack traces of all server threads. Allows developers to introspect the server state.
|
||||
|
||||
To analyze stack frames, use the `addressToLine`, `addressToSymbol` and `demangle` [introspection functions](../../sql-reference/functions/introspection.md).
|
||||
To analyze stack frames, use the `addressToLine`, `addressToLineWithInlines`, `addressToSymbol` and `demangle` [introspection functions](../../sql-reference/functions/introspection.md).
|
||||
|
||||
Columns:
|
||||
|
||||
|
@ -4,7 +4,7 @@ Contains stack traces collected by the sampling query profiler.
|
||||
|
||||
ClickHouse creates this table when the [trace_log](../../operations/server-configuration-parameters/settings.md#server_configuration_parameters-trace_log) server configuration section is set. Also the [query_profiler_real_time_period_ns](../../operations/settings/settings.md#query_profiler_real_time_period_ns) and [query_profiler_cpu_time_period_ns](../../operations/settings/settings.md#query_profiler_cpu_time_period_ns) settings should be set.
|
||||
|
||||
To analyze logs, use the `addressToLine`, `addressToSymbol` and `demangle` introspection functions.
|
||||
To analyze logs, use the `addressToLine`, `addressToLineWithInlines`, `addressToSymbol` and `demangle` introspection functions.
|
||||
|
||||
Columns:
|
||||
|
||||
|
@ -113,6 +113,111 @@ trace_source_code_lines: /lib/x86_64-linux-gnu/libpthread-2.27.so
|
||||
/build/glibc-OTsEL5/glibc-2.27/misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:97
|
||||
```
|
||||
|
||||
## addressToLineWithInlines {#addresstolinewithinlines}
|
||||
|
||||
Similar to `addressToLine`, but it will return an Array with all inline functions, and will be much slower as a price.
|
||||
|
||||
If you use official ClickHouse packages, you need to install the `clickhouse-common-static-dbg` package.
|
||||
|
||||
**Syntax**
|
||||
|
||||
``` sql
|
||||
addressToLineWithInlines(address_of_binary_instruction)
|
||||
```
|
||||
|
||||
**Arguments**
|
||||
|
||||
- `address_of_binary_instruction` ([UInt64](../../sql-reference/data-types/int-uint.md)) — Address of instruction in a running process.
|
||||
|
||||
**Returned value**
|
||||
|
||||
- Array which first element is source code filename and the line number in this file delimited by colon. And from second element, inline functions' source code filename and line number and function name are listed.
|
||||
|
||||
- Array with single element which is name of a binary, if the function couldn’t find the debug information.
|
||||
|
||||
- Empty array, if the address is not valid.
|
||||
|
||||
Type: [Array(String)](../../sql-reference/data-types/array.md).
|
||||
|
||||
**Example**
|
||||
|
||||
Enabling introspection functions:
|
||||
|
||||
``` sql
|
||||
SET allow_introspection_functions=1;
|
||||
```
|
||||
|
||||
Applying the function to address.
|
||||
|
||||
```sql
|
||||
SELECT addressToLineWithInlines(531055181::UInt64);
|
||||
```
|
||||
|
||||
``` text
|
||||
┌─addressToLineWithInlines(CAST('531055181', 'UInt64'))────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ ['./src/Functions/addressToLineWithInlines.cpp:98','./build_normal_debug/./src/Functions/addressToLineWithInlines.cpp:176:DB::(anonymous namespace)::FunctionAddressToLineWithInlines::implCached(unsigned long) const'] │
|
||||
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Applying the function to the whole stack trace:
|
||||
|
||||
``` sql
|
||||
SELECT
|
||||
ta, addressToLineWithInlines(arrayJoin(trace) as ta)
|
||||
FROM system.trace_log
|
||||
WHERE
|
||||
query_id = '5e173544-2020-45de-b645-5deebe2aae54';
|
||||
```
|
||||
|
||||
The [arrayJoin](../../sql-reference/functions/array-functions.md#array-functions-join) functions will split array to rows.
|
||||
|
||||
``` text
|
||||
┌────────ta─┬─addressToLineWithInlines(arrayJoin(trace))───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 365497529 │ ['./build_normal_debug/./contrib/libcxx/include/string_view:252'] │
|
||||
│ 365593602 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:191'] │
|
||||
│ 365593866 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365592528 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365591003 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:477'] │
|
||||
│ 365590479 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:442'] │
|
||||
│ 365590600 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:457'] │
|
||||
│ 365598941 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365607098 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365590571 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:451'] │
|
||||
│ 365598941 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365607098 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365590571 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:451'] │
|
||||
│ 365598941 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365607098 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365590571 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:451'] │
|
||||
│ 365598941 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:0'] │
|
||||
│ 365597289 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:807'] │
|
||||
│ 365599840 │ ['./build_normal_debug/./src/Common/Dwarf.cpp:1118'] │
|
||||
│ 531058145 │ ['./build_normal_debug/./src/Functions/addressToLineWithInlines.cpp:152'] │
|
||||
│ 531055181 │ ['./src/Functions/addressToLineWithInlines.cpp:98','./build_normal_debug/./src/Functions/addressToLineWithInlines.cpp:176:DB::(anonymous namespace)::FunctionAddressToLineWithInlines::implCached(unsigned long) const'] │
|
||||
│ 422333613 │ ['./build_normal_debug/./src/Functions/IFunctionAdaptors.h:21'] │
|
||||
│ 586866022 │ ['./build_normal_debug/./src/Functions/IFunction.cpp:216'] │
|
||||
│ 586869053 │ ['./build_normal_debug/./src/Functions/IFunction.cpp:264'] │
|
||||
│ 586873237 │ ['./build_normal_debug/./src/Functions/IFunction.cpp:334'] │
|
||||
│ 597901620 │ ['./build_normal_debug/./src/Interpreters/ExpressionActions.cpp:601'] │
|
||||
│ 597898534 │ ['./build_normal_debug/./src/Interpreters/ExpressionActions.cpp:718'] │
|
||||
│ 630442912 │ ['./build_normal_debug/./src/Processors/Transforms/ExpressionTransform.cpp:23'] │
|
||||
│ 546354050 │ ['./build_normal_debug/./src/Processors/ISimpleTransform.h:38'] │
|
||||
│ 626026993 │ ['./build_normal_debug/./src/Processors/ISimpleTransform.cpp:89'] │
|
||||
│ 626294022 │ ['./build_normal_debug/./src/Processors/Executors/ExecutionThreadContext.cpp:45'] │
|
||||
│ 626293730 │ ['./build_normal_debug/./src/Processors/Executors/ExecutionThreadContext.cpp:63'] │
|
||||
│ 626169525 │ ['./build_normal_debug/./src/Processors/Executors/PipelineExecutor.cpp:213'] │
|
||||
│ 626170308 │ ['./build_normal_debug/./src/Processors/Executors/PipelineExecutor.cpp:178'] │
|
||||
│ 626166348 │ ['./build_normal_debug/./src/Processors/Executors/PipelineExecutor.cpp:329'] │
|
||||
│ 626163461 │ ['./build_normal_debug/./src/Processors/Executors/PipelineExecutor.cpp:84'] │
|
||||
│ 626323536 │ ['./build_normal_debug/./src/Processors/Executors/PullingAsyncPipelineExecutor.cpp:85'] │
|
||||
│ 626323277 │ ['./build_normal_debug/./src/Processors/Executors/PullingAsyncPipelineExecutor.cpp:112'] │
|
||||
│ 626323133 │ ['./build_normal_debug/./contrib/libcxx/include/type_traits:3682'] │
|
||||
│ 626323041 │ ['./build_normal_debug/./contrib/libcxx/include/tuple:1415'] │
|
||||
└───────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
```
|
||||
|
||||
|
||||
## addressToSymbol {#addresstosymbol}
|
||||
|
||||
Converts virtual memory address inside ClickHouse server process to the symbol from ClickHouse object files.
|
||||
|
@ -172,6 +172,7 @@ Hierarchy of privileges:
|
||||
- `SYSTEM FLUSH LOGS`
|
||||
- [INTROSPECTION](#grant-introspection)
|
||||
- `addressToLine`
|
||||
- `addressToLineWithInlines`
|
||||
- `addressToSymbol`
|
||||
- `demangle`
|
||||
- [SOURCES](#grant-sources)
|
||||
@ -430,6 +431,7 @@ Allows using [introspection](../../operations/optimizing-performance/sampling-qu
|
||||
|
||||
- `INTROSPECTION`. Level: `GROUP`. Aliases: `INTROSPECTION FUNCTIONS`
|
||||
- `addressToLine`. Level: `GLOBAL`
|
||||
- `addressToLineWithInlines`. Level: `GLOBAL`
|
||||
- `addressToSymbol`. Level: `GLOBAL`
|
||||
- `demangle`. Level: `GLOBAL`
|
||||
|
||||
|
@ -166,6 +166,7 @@ enum class AccessType
|
||||
M(dictGet, "dictHas, dictGetHierarchy, dictIsIn", DICTIONARY, ALL) /* allows to execute functions dictGet(), dictHas(), dictGetHierarchy(), dictIsIn() */\
|
||||
\
|
||||
M(addressToLine, "", GLOBAL, INTROSPECTION) /* allows to execute function addressToLine() */\
|
||||
M(addressToLineWithInlines, "", GLOBAL, INTROSPECTION) /* allows to execute function addressToLineWithInlines() */\
|
||||
M(addressToSymbol, "", GLOBAL, INTROSPECTION) /* allows to execute function addressToSymbol() */\
|
||||
M(demangle, "", GLOBAL, INTROSPECTION) /* allows to execute function demangle() */\
|
||||
M(INTROSPECTION, "INTROSPECTION FUNCTIONS", GROUP, ALL) /* allows to execute functions addressToLine(), addressToSymbol(), demangle()*/\
|
||||
|
@ -1,153 +1,58 @@
|
||||
#if defined(__ELF__) && !defined(__FreeBSD__)
|
||||
|
||||
#include <Common/Dwarf.h>
|
||||
#include <Common/SymbolIndex.h>
|
||||
#include <Common/HashTable/HashMap.h>
|
||||
#include <Common/Arena.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <Functions/IFunction.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <IO/WriteBufferFromArena.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Access/Common/AccessFlags.h>
|
||||
#include <Interpreters/Context.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <Functions/addressToLine.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class FunctionAddressToLine : public IFunction
|
||||
class FunctionAddressToLine: public FunctionAddressToLineBase<StringRef, Dwarf::LocationInfoMode::FAST>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "addressToLine";
|
||||
String getName() const override { return name; }
|
||||
static FunctionPtr create(ContextPtr context)
|
||||
{
|
||||
context->checkAccess(AccessType::addressToLine);
|
||||
return std::make_shared<FunctionAddressToLine>();
|
||||
}
|
||||
|
||||
String getName() const override
|
||||
protected:
|
||||
DataTypePtr getDataType() const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
size_t getNumberOfArguments() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
if (arguments.size() != 1)
|
||||
throw Exception("Function " + getName() + " needs exactly one argument; passed "
|
||||
+ toString(arguments.size()) + ".", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
const auto & type = arguments[0].type;
|
||||
|
||||
if (!WhichDataType(type.get()).isUInt64())
|
||||
throw Exception("The only argument for function " + getName() + " must be UInt64. Found "
|
||||
+ type->getName() + " instead.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
return std::make_shared<DataTypeString>();
|
||||
}
|
||||
|
||||
bool useDefaultImplementationForConstants() const override
|
||||
ColumnPtr getResultColumn(const typename ColumnVector<UInt64>::Container & data, size_t input_rows_count) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
||||
{
|
||||
const ColumnPtr & column = arguments[0].column;
|
||||
const ColumnUInt64 * column_concrete = checkAndGetColumn<ColumnUInt64>(column.get());
|
||||
|
||||
if (!column_concrete)
|
||||
throw Exception("Illegal column " + column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
const typename ColumnVector<UInt64>::Container & data = column_concrete->getData();
|
||||
auto result_column = ColumnString::create();
|
||||
|
||||
for (size_t i = 0; i < input_rows_count; ++i)
|
||||
{
|
||||
StringRef res_str = implCached(data[i]);
|
||||
result_column->insertData(res_str.data, res_str.size);
|
||||
}
|
||||
|
||||
return result_column;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Cache
|
||||
void setResult(StringRef & result, const Dwarf::LocationInfo & location, const std::vector<Dwarf::SymbolizedFrame> &) const override
|
||||
{
|
||||
std::mutex mutex;
|
||||
Arena arena;
|
||||
using Map = HashMap<uintptr_t, StringRef>;
|
||||
Map map;
|
||||
std::unordered_map<std::string, Dwarf> dwarfs;
|
||||
};
|
||||
const char * arena_begin = nullptr;
|
||||
WriteBufferFromArena out(cache.arena, arena_begin);
|
||||
|
||||
mutable Cache cache;
|
||||
writeString(location.file.toString(), out);
|
||||
writeChar(':', out);
|
||||
writeIntText(location.line, out);
|
||||
|
||||
StringRef impl(uintptr_t addr) const
|
||||
{
|
||||
auto symbol_index_ptr = SymbolIndex::instance();
|
||||
const SymbolIndex & symbol_index = *symbol_index_ptr;
|
||||
|
||||
if (const auto * object = symbol_index.findObject(reinterpret_cast<const void *>(addr)))
|
||||
{
|
||||
auto dwarf_it = cache.dwarfs.try_emplace(object->name, object->elf).first;
|
||||
if (!std::filesystem::exists(object->name))
|
||||
return {};
|
||||
|
||||
Dwarf::LocationInfo location;
|
||||
std::vector<Dwarf::SymbolizedFrame> frames; // NOTE: not used in FAST mode.
|
||||
if (dwarf_it->second.findAddress(addr - uintptr_t(object->address_begin), location, Dwarf::LocationInfoMode::FAST, frames))
|
||||
{
|
||||
const char * arena_begin = nullptr;
|
||||
WriteBufferFromArena out(cache.arena, arena_begin);
|
||||
|
||||
writeString(location.file.toString(), out);
|
||||
writeChar(':', out);
|
||||
writeIntText(location.line, out);
|
||||
|
||||
return out.complete();
|
||||
}
|
||||
else
|
||||
{
|
||||
return object->name;
|
||||
}
|
||||
}
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
StringRef implCached(uintptr_t addr) const
|
||||
{
|
||||
Cache::Map::LookupResult it;
|
||||
bool inserted;
|
||||
std::lock_guard lock(cache.mutex);
|
||||
cache.map.emplace(addr, it, inserted);
|
||||
if (inserted)
|
||||
it->getMapped() = impl(addr);
|
||||
return it->getMapped();
|
||||
result = out.complete();
|
||||
}
|
||||
};
|
||||
|
||||
|
133
src/Functions/addressToLine.h
Normal file
133
src/Functions/addressToLine.h
Normal file
@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
#if defined(__ELF__) && !defined(__FreeBSD__)
|
||||
|
||||
#include <Common/Dwarf.h>
|
||||
#include <Common/SymbolIndex.h>
|
||||
#include <Common/HashTable/HashMap.h>
|
||||
#include <Common/Arena.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnArray.h>
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <Functions/IFunction.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <IO/WriteBufferFromArena.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Access/Common/AccessFlags.h>
|
||||
#include <Interpreters/Context.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
}
|
||||
|
||||
template <typename ResultT, Dwarf::LocationInfoMode locationInfoMode>
|
||||
class FunctionAddressToLineBase : public IFunction
|
||||
{
|
||||
public:
|
||||
|
||||
size_t getNumberOfArguments() const override { return 1; }
|
||||
|
||||
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
if (arguments.size() != 1)
|
||||
throw Exception(
|
||||
"Function " + getName() + " needs exactly one argument; passed " + toString(arguments.size()) + ".",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
const auto & type = arguments[0].type;
|
||||
|
||||
if (!WhichDataType(type.get()).isUInt64())
|
||||
throw Exception(
|
||||
"The only argument for function " + getName() + " must be UInt64. Found " + type->getName() + " instead.",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
|
||||
return getDataType();
|
||||
}
|
||||
|
||||
bool useDefaultImplementationForConstants() const override { return true; }
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
||||
{
|
||||
const ColumnPtr & column = arguments[0].column;
|
||||
const ColumnUInt64 * column_concrete = checkAndGetColumn<ColumnUInt64>(column.get());
|
||||
|
||||
if (!column_concrete)
|
||||
throw Exception(
|
||||
"Illegal column " + column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
const typename ColumnVector<UInt64>::Container & data = column_concrete->getData();
|
||||
return getResultColumn(data, input_rows_count);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual DataTypePtr getDataType() const = 0;
|
||||
virtual ColumnPtr getResultColumn(const typename ColumnVector<UInt64>::Container & data, size_t input_rows_count) const = 0;
|
||||
virtual void
|
||||
setResult(ResultT & result, const Dwarf::LocationInfo & location, const std::vector<Dwarf::SymbolizedFrame> & frames) const = 0;
|
||||
|
||||
struct Cache
|
||||
{
|
||||
std::mutex mutex;
|
||||
Arena arena;
|
||||
using Map = HashMap<uintptr_t, ResultT>;
|
||||
Map map;
|
||||
std::unordered_map<std::string, Dwarf> dwarfs;
|
||||
};
|
||||
|
||||
mutable Cache cache;
|
||||
|
||||
ResultT impl(uintptr_t addr) const
|
||||
{
|
||||
auto symbol_index_ptr = SymbolIndex::instance();
|
||||
const SymbolIndex & symbol_index = *symbol_index_ptr;
|
||||
|
||||
if (const auto * object = symbol_index.findObject(reinterpret_cast<const void *>(addr)))
|
||||
{
|
||||
auto dwarf_it = cache.dwarfs.try_emplace(object->name, object->elf).first;
|
||||
if (!std::filesystem::exists(object->name))
|
||||
return {};
|
||||
|
||||
Dwarf::LocationInfo location;
|
||||
std::vector<Dwarf::SymbolizedFrame> frames; // NOTE: not used in FAST mode.
|
||||
ResultT result;
|
||||
if (dwarf_it->second.findAddress(addr - uintptr_t(object->address_begin), location, locationInfoMode, frames))
|
||||
{
|
||||
setResult(result, location, frames);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return {object->name};
|
||||
}
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
ResultT implCached(uintptr_t addr) const
|
||||
{
|
||||
typename Cache::Map::LookupResult it;
|
||||
bool inserted;
|
||||
std::lock_guard lock(cache.mutex);
|
||||
cache.map.emplace(addr, it, inserted);
|
||||
if (inserted)
|
||||
it->getMapped() = impl(addr);
|
||||
return it->getMapped();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
99
src/Functions/addressToLineWithInlines.cpp
Normal file
99
src/Functions/addressToLineWithInlines.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
#if defined(__ELF__) && !defined(__FreeBSD__)
|
||||
|
||||
#include <Common/Dwarf.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnArray.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <IO/WriteBufferFromArena.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Access/Common/AccessFlags.h>
|
||||
|
||||
#include <Functions/addressToLine.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class FunctionAddressToLineWithInlines: public FunctionAddressToLineBase<StringRefs, Dwarf::LocationInfoMode::FULL_WITH_INLINE>
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = "addressToLineWithInlines";
|
||||
String getName() const override { return name; }
|
||||
static FunctionPtr create(ContextPtr context)
|
||||
{
|
||||
context->checkAccess(AccessType::addressToLineWithInlines);
|
||||
return std::make_shared<FunctionAddressToLineWithInlines>();
|
||||
}
|
||||
|
||||
protected:
|
||||
DataTypePtr getDataType() const override
|
||||
{
|
||||
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>());
|
||||
}
|
||||
|
||||
ColumnPtr getResultColumn(const typename ColumnVector<UInt64>::Container & data, size_t input_rows_count) const override
|
||||
{
|
||||
auto result_column = ColumnArray::create(ColumnString::create());
|
||||
ColumnString & result_strings = typeid_cast<ColumnString &>(result_column->getData());
|
||||
ColumnArray::Offsets & result_offsets = result_column->getOffsets();
|
||||
|
||||
ColumnArray::Offset current_offset = 0;
|
||||
|
||||
for (size_t i = 0; i < input_rows_count; ++i)
|
||||
{
|
||||
StringRefs res = implCached(data[i]);
|
||||
for (auto & r : res)
|
||||
result_strings.insertData(r.data, r.size);
|
||||
current_offset += res.size();
|
||||
result_offsets.push_back(current_offset);
|
||||
}
|
||||
|
||||
return result_column;
|
||||
}
|
||||
|
||||
void setResult(StringRefs & result, const Dwarf::LocationInfo & location, const std::vector<Dwarf::SymbolizedFrame> & inline_frames) const override
|
||||
{
|
||||
|
||||
appendLocationToResult(result, location, nullptr);
|
||||
for (const auto & inline_frame : inline_frames)
|
||||
appendLocationToResult(result, inline_frame.location, &inline_frame);
|
||||
}
|
||||
private:
|
||||
|
||||
inline ALWAYS_INLINE void appendLocationToResult(StringRefs & result, const Dwarf::LocationInfo & location, const Dwarf::SymbolizedFrame * frame) const
|
||||
{
|
||||
const char * arena_begin = nullptr;
|
||||
WriteBufferFromArena out(cache.arena, arena_begin);
|
||||
|
||||
writeString(location.file.toString(), out);
|
||||
writeChar(':', out);
|
||||
writeIntText(location.line, out);
|
||||
|
||||
if (frame)
|
||||
{
|
||||
writeChar(':', out);
|
||||
int status = 0;
|
||||
writeString(demangle(frame->name, status), out);
|
||||
}
|
||||
|
||||
result.emplace_back(out.complete());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void registerFunctionAddressToLineWithInlines(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionAddressToLineWithInlines>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -6,6 +6,7 @@ class FunctionFactory;
|
||||
#if defined(OS_LINUX)
|
||||
void registerFunctionAddressToSymbol(FunctionFactory & factory);
|
||||
void registerFunctionAddressToLine(FunctionFactory & factory);
|
||||
void registerFunctionAddressToLineWithInlines(FunctionFactory & factory);
|
||||
#endif
|
||||
|
||||
void registerFunctionDemangle(FunctionFactory & factory);
|
||||
@ -17,6 +18,7 @@ void registerFunctionsIntrospection(FunctionFactory & factory)
|
||||
#if defined(OS_LINUX)
|
||||
registerFunctionAddressToSymbol(factory);
|
||||
registerFunctionAddressToLine(factory);
|
||||
registerFunctionAddressToLineWithInlines(factory);
|
||||
#endif
|
||||
registerFunctionDemangle(factory);
|
||||
registerFunctionTrap(factory);
|
||||
|
@ -118,6 +118,7 @@ SYSTEM THREAD FUZZER ['SYSTEM START THREAD FUZZER','SYSTEM STOP THREAD FUZZER','
|
||||
SYSTEM [] \N ALL
|
||||
dictGet ['dictHas','dictGetHierarchy','dictIsIn'] DICTIONARY ALL
|
||||
addressToLine [] GLOBAL INTROSPECTION
|
||||
addressToLineWithInlines [] GLOBAL INTROSPECTION
|
||||
addressToSymbol [] GLOBAL INTROSPECTION
|
||||
demangle [] GLOBAL INTROSPECTION
|
||||
INTROSPECTION ['INTROSPECTION FUNCTIONS'] \N ALL
|
||||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,2 @@
|
||||
10000000000
|
||||
has inlines: 1
|
25
tests/queries/0_stateless/02161_addressToLineWithInlines.sql
Normal file
25
tests/queries/0_stateless/02161_addressToLineWithInlines.sql
Normal file
@ -0,0 +1,25 @@
|
||||
-- Tags: no-tsan, no-asan, no-ubsan, no-msan, no-debug
|
||||
|
||||
|
||||
SELECT addressToLineWithInlines(1); -- { serverError 446 }
|
||||
|
||||
SET allow_introspection_functions = 1;
|
||||
SET query_profiler_real_time_period_ns = 0;
|
||||
SET query_profiler_cpu_time_period_ns = 1000000;
|
||||
SET log_queries = 1;
|
||||
SELECT count() FROM numbers_mt(10000000000) SETTINGS log_comment='02161_test_case';
|
||||
SET log_queries = 0;
|
||||
SET query_profiler_cpu_time_period_ns = 0;
|
||||
SYSTEM FLUSH LOGS;
|
||||
|
||||
WITH
|
||||
lineWithInlines AS
|
||||
(
|
||||
SELECT DISTINCT addressToLineWithInlines(arrayJoin(trace)) AS lineWithInlines FROM system.trace_log WHERE query_id =
|
||||
(
|
||||
SELECT query_id FROM system.query_log WHERE current_database = currentDatabase() AND log_comment='02161_test_case' ORDER BY event_time DESC LIMIT 1
|
||||
)
|
||||
)
|
||||
SELECT 'has inlines:', or(max(length(lineWithInlines)) > 1, max(locate(lineWithInlines[1], ':')) = 0) FROM lineWithInlines SETTINGS short_circuit_function_evaluation='enable';
|
||||
-- `max(length(lineWithInlines)) > 1` check there is any inlines.
|
||||
-- `max(locate(lineWithInlines[1], ':')) = 0` check whether none could get a symbol.
|
Loading…
Reference in New Issue
Block a user