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 | |
23 | namespace DB |
24 | { |
25 | |
26 | namespace 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 | |
34 | class FunctionAddressToLine : public IFunction |
35 | { |
36 | public: |
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 | |
96 | private: |
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 | |
148 | void registerFunctionAddressToLine(FunctionFactory & factory) |
149 | { |
150 | factory.registerFunction<FunctionAddressToLine>(); |
151 | } |
152 | |
153 | } |
154 | |
155 | #endif |
156 | |