1#if defined(__ELF__) && !defined(__FreeBSD__)
2
3#include <Common/Elf.h>
4#include <Common/Dwarf.h>
5#include <Common/SymbolIndex.h>
6#include <Common/HashTable/HashMap.h>
7#include <Common/Arena.h>
8#include <Columns/ColumnString.h>
9#include <Columns/ColumnsNumber.h>
10#include <DataTypes/DataTypeString.h>
11#include <Functions/IFunctionImpl.h>
12#include <Functions/FunctionHelpers.h>
13#include <Functions/FunctionFactory.h>
14#include <IO/WriteBufferFromArena.h>
15#include <IO/WriteHelpers.h>
16#include <Interpreters/Context.h>
17
18#include <mutex>
19#include <filesystem>
20#include <unordered_map>
21
22
23namespace DB
24{
25
26namespace ErrorCodes
27{
28 extern const int ILLEGAL_COLUMN;
29 extern const int ILLEGAL_TYPE_OF_ARGUMENT;
30 extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
31 extern const int FUNCTION_NOT_ALLOWED;
32}
33
34class FunctionAddressToLine : public IFunction
35{
36public:
37 static constexpr auto name = "addressToLine";
38 static FunctionPtr create(const Context & context)
39 {
40 if (!context.getSettingsRef().allow_introspection_functions)
41 throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
42
43 return std::make_shared<FunctionAddressToLine>();
44 }
45
46 String getName() const override
47 {
48 return name;
49 }
50
51 size_t getNumberOfArguments() const override
52 {
53 return 1;
54 }
55
56 DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
57 {
58 if (arguments.size() != 1)
59 throw Exception("Function " + getName() + " needs exactly one argument; passed "
60 + toString(arguments.size()) + ".", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
61
62 const auto & type = arguments[0].type;
63
64 if (!WhichDataType(type.get()).isUInt64())
65 throw Exception("The only argument for function " + getName() + " must be UInt64. Found "
66 + type->getName() + " instead.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
67
68 return std::make_shared<DataTypeString>();
69 }
70
71 bool useDefaultImplementationForConstants() const override
72 {
73 return true;
74 }
75
76 void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
77 {
78 const ColumnPtr & column = block.getByPosition(arguments[0]).column;
79 const ColumnUInt64 * column_concrete = checkAndGetColumn<ColumnUInt64>(column.get());
80
81 if (!column_concrete)
82 throw Exception("Illegal column " + column->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN);
83
84 const typename ColumnVector<UInt64>::Container & data = column_concrete->getData();
85 auto result_column = ColumnString::create();
86
87 for (size_t i = 0; i < input_rows_count; ++i)
88 {
89 StringRef res_str = implCached(data[i]);
90 result_column->insertData(res_str.data, res_str.size);
91 }
92
93 block.getByPosition(result).column = std::move(result_column);
94 }
95
96private:
97 std::mutex mutex;
98 Arena arena;
99 using Map = HashMap<uintptr_t, StringRef>;
100 Map map;
101 std::unordered_map<std::string, Dwarf> dwarfs;
102
103 StringRef impl(uintptr_t addr)
104 {
105 const SymbolIndex & symbol_index = SymbolIndex::instance();
106
107 if (auto object = symbol_index.findObject(reinterpret_cast<const void *>(addr)))
108 {
109 auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first;
110 if (!std::filesystem::exists(object->name))
111 return {};
112
113 Dwarf::LocationInfo location;
114 if (dwarf_it->second.findAddress(addr - uintptr_t(object->address_begin), location, Dwarf::LocationInfoMode::FAST))
115 {
116 const char * arena_begin = nullptr;
117 WriteBufferFromArena out(arena, arena_begin);
118
119 writeString(location.file.toString(), out);
120 writeChar(':', out);
121 writeIntText(location.line, out);
122
123 StringRef out_str = out.finish();
124 out_str.data = arena.insert(out_str.data, out_str.size);
125 return out_str;
126 }
127 else
128 {
129 return object->name;
130 }
131 }
132 else
133 return {};
134 }
135
136 StringRef implCached(uintptr_t addr)
137 {
138 Map::LookupResult it;
139 bool inserted;
140 std::lock_guard lock(mutex);
141 map.emplace(addr, it, inserted);
142 if (inserted)
143 it->getMapped() = impl(addr);
144 return it->getMapped();
145 }
146};
147
148void registerFunctionAddressToLine(FunctionFactory & factory)
149{
150 factory.registerFunction<FunctionAddressToLine>();
151}
152
153}
154
155#endif
156