ClickHouse
163 строки · 5.1 Кб
1#if defined(SANITIZE_COVERAGE)
2
3#include <DataTypes/DataTypeArray.h>
4#include <DataTypes/DataTypesNumber.h>
5#include <Columns/ColumnArray.h>
6#include <Columns/ColumnVector.h>
7#include <Columns/ColumnsNumber.h>
8#include <Columns/ColumnConst.h>
9#include <Columns/ColumnsNumber.h>
10#include <Functions/FunctionFactory.h>
11#include <Functions/IFunction.h>
12#include <Interpreters/Context.h>
13
14#include <base/coverage.h>
15
16
17namespace DB
18{
19
20namespace
21{
22
23enum class Kind
24{
25Current,
26Cumulative,
27All
28};
29
30/** If ClickHouse is build with coverage instrumentation, returns an array
31* of currently accumulated (`coverageCurrent`)
32* or accumulated since the startup (`coverageCumulative`)
33* or all possible (`coverageAll`) unique code addresses.
34*/
35class FunctionCoverage : public IFunction
36{
37private:
38Kind kind;
39
40public:
41String getName() const override
42{
43return kind == Kind::Current ? "coverage" : "coverageAll";
44}
45
46explicit FunctionCoverage(Kind kind_) : kind(kind_)
47{
48}
49
50bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override
51{
52return false;
53}
54
55size_t getNumberOfArguments() const override
56{
57return 0;
58}
59
60bool isDeterministic() const override
61{
62return false;
63}
64
65DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override
66{
67return std::make_shared<DataTypeArray>(std::make_shared<DataTypeUInt64>());
68}
69
70ColumnPtr executeImpl(const ColumnsWithTypeAndName &, const DataTypePtr &, size_t input_rows_count) const override
71{
72auto coverage_table = kind == Kind::Current
73? getCurrentCoverage()
74: (kind == Kind::Cumulative
75? getCumulativeCoverage()
76: getAllInstrumentedAddresses());
77
78auto column_addresses = ColumnUInt64::create();
79auto & data = column_addresses->getData();
80
81for (auto ptr : coverage_table)
82if (ptr)
83data.push_back(ptr);
84
85auto column_array = ColumnArray::create(
86std::move(column_addresses),
87ColumnArray::ColumnOffsets::create(1, data.size()));
88
89return ColumnConst::create(std::move(column_array), input_rows_count);
90}
91};
92
93}
94
95REGISTER_FUNCTION(Coverage)
96{
97factory.registerFunction("coverageCurrent", [](ContextPtr){ return std::make_shared<FunctionCoverage>(Kind::Current); },
98FunctionDocumentation
99{
100.description=R"(
101This function is only available if ClickHouse was built with the SANITIZE_COVERAGE=1 option.
102
103It returns an array of unique addresses (a subset of the instrumented points in code) in the code
104encountered at runtime after the previous coverage reset (with the `SYSTEM RESET COVERAGE` query) or after server startup.
105
106[example:functions]
107
108The order of array elements is undetermined.
109
110You can use another function, `coverageAll` to find all instrumented addresses in the code to compare and calculate the percentage.
111
112You can process the addresses with the `addressToSymbol` (possibly with `demangle`) and `addressToLine` functions
113to calculate symbol-level, file-level, or line-level coverage.
114
115If you run multiple tests sequentially and reset the coverage with the `SYSTEM RESET COVERAGE` query between the tests,
116you can obtain a coverage information for every test in isolation, to find which functions are covered by which tests and vise-versa.
117
118By default, every *basic block* in the code is covered, which roughly means - a sequence of instructions without jumps,
119e.g. a body of for loop without ifs, or a single branch of if.
120
121See https://clang.llvm.org/docs/SanitizerCoverage.html for more information.
122)",
123.examples{
124{"functions", "SELECT DISTINCT demangle(addressToSymbol(arrayJoin(coverageCurrent())))", ""}},
125.categories{"Introspection"}
126});
127
128factory.registerFunction("coverageCumulative", [](ContextPtr){ return std::make_shared<FunctionCoverage>(Kind::Cumulative); },
129FunctionDocumentation
130{
131.description=R"(
132This function is only available if ClickHouse was built with the SANITIZE_COVERAGE=1 option.
133
134It returns an array of unique addresses (a subset of the instrumented points in code) in the code
135encountered at runtime after server startup.
136
137In contrast to `coverageCurrent` it cannot be reset with the `SYSTEM RESET COVERAGE`.
138
139See the `coverageCurrent` function for the details.
140)",
141.categories{"Introspection"}
142});
143
144factory.registerFunction("coverageAll", [](ContextPtr){ return std::make_shared<FunctionCoverage>(Kind::All); },
145FunctionDocumentation
146{
147.description=R"(
148This function is only available if ClickHouse was built with the SANITIZE_COVERAGE=1 option.
149
150It returns an array of all unique addresses in the code instrumented for coverage
151- all possible addresses that can appear in the result of the `coverage` function.
152
153You can use this function, and the `coverage` function to compare and calculate the coverage percentage.
154
155See the `coverageCurrent` function for the details.
156)",
157.categories{"Introspection"}
158});
159}
160
161}
162
163#endif
164