Skip to content

Commit 1a68845

Browse files
authored
Merge pull request #17892 from github/calumgrant/telemetry
C++: Initial telemetry queries
2 parents 349518b + 91b1cb8 commit 1a68845

26 files changed

+471
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Compiler errors
3+
* @description A count of all compiler errors, grouped by error text.
4+
* @kind metric
5+
* @tags summary telemetry
6+
* @id cpp/telemetry/compiler-errors
7+
*/
8+
9+
import Metrics
10+
11+
from CppMetrics::ErrorCount m
12+
where RankMetric<CppMetrics::ErrorCount>::getRank(m) <= 50
13+
select m.toString(), m.getValue()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @name Database quality
3+
* @description Metrics that indicate the quality of the database.
4+
* @kind metric
5+
* @tags summary telemetry
6+
* @id cpp/telemetry/database-quality
7+
*/
8+
9+
import Metrics
10+
11+
from QualityMetric m
12+
select m.toString(), m.getValue()

cpp/ql/src/Telemetry/Diagnostics.qll

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import cpp
2+
3+
/**
4+
* A syntax error.
5+
*/
6+
class SyntaxError extends CompilerError {
7+
SyntaxError() {
8+
this.getTag().matches("exp_%") or
9+
this.getTag() =
10+
[
11+
"bad_data_member_initialization", "bad_pure_specifier", "bad_return", "bad_uuid_string",
12+
"literal_without_initializer", "missing_class_definition", "missing_exception_declaration",
13+
"nonstd_const_member_decl_not_allowed", "operator_name_not_allowed",
14+
"wide_string_invalid_in_asm"
15+
]
16+
}
17+
}
18+
19+
/**
20+
* A cannot open file error.
21+
* Typically this is due to a missing include.
22+
*/
23+
class CannotOpenFileError extends CompilerError {
24+
CannotOpenFileError() { this.hasTag(["cannot_open_file", "cannot_open_file_reason"]) }
25+
26+
string getIncludedFile() {
27+
result = this.getMessage().regexpCapture("cannot open source file '([^']+)'", 1)
28+
}
29+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @name Extraction metrics
3+
* @description Raw metrics relating to extraction.
4+
* @kind metric
5+
* @tags summary telemetry
6+
* @id cpp/telemetry/extraction-metrics
7+
*/
8+
9+
import Metrics
10+
11+
from ExtractionMetric m
12+
select m.toString(), m.getValue()

cpp/ql/src/Telemetry/Metrics.qll

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
import cpp
2+
import Diagnostics
3+
4+
/**
5+
* A metric is a string with a value.
6+
*/
7+
abstract class Metric extends string {
8+
bindingset[this]
9+
Metric() { any() }
10+
}
11+
12+
/**
13+
* A metric that we want to report in cpp/telemetry/extraction-metrics
14+
*/
15+
abstract class ExtractionMetric extends Metric {
16+
bindingset[this]
17+
ExtractionMetric() { any() }
18+
19+
/** Gets the value of this metric. */
20+
abstract int getValue();
21+
}
22+
23+
/**
24+
* A metric that provides a baseline for a SuccessMetric.
25+
*/
26+
abstract class BaseMetric extends ExtractionMetric {
27+
bindingset[this]
28+
BaseMetric() { any() }
29+
}
30+
31+
/**
32+
* A metric that is relative to another metric,
33+
* so can be used to calculate percentages.
34+
*
35+
* For clarity, metrics should express success,
36+
* so higher values means better.
37+
*/
38+
abstract class SuccessMetric extends ExtractionMetric {
39+
bindingset[this]
40+
SuccessMetric() { any() }
41+
42+
/** Gets the metric this is relative to. */
43+
abstract BaseMetric getBaseline();
44+
}
45+
46+
/**
47+
* A metric used to report database quality.
48+
*/
49+
class QualityMetric extends Metric {
50+
BaseMetric baseMetric;
51+
SuccessMetric relativeMetric;
52+
53+
QualityMetric() {
54+
baseMetric = relativeMetric.getBaseline() and this = "Percentage of " + relativeMetric
55+
}
56+
57+
float getValue() {
58+
baseMetric.getValue() > 0 and
59+
result = 100.0 * relativeMetric.getValue() / baseMetric.getValue()
60+
}
61+
}
62+
63+
signature class RankedMetric extends Metric {
64+
int getValue();
65+
}
66+
67+
module RankMetric<RankedMetric M> {
68+
int getRank(M s) { s = rank[result](M m | | m order by m.getValue() desc) }
69+
}
70+
71+
/** Various metrics we want to report. */
72+
module CppMetrics {
73+
class Compilations extends BaseMetric {
74+
Compilations() { this = "compilations" }
75+
76+
override int getValue() { result = count(Compilation c) }
77+
}
78+
79+
class SourceAndHeaderFiles extends BaseMetric {
80+
SourceAndHeaderFiles() { this = "source/header files" }
81+
82+
override int getValue() { result = count(File f | f.fromSource()) }
83+
}
84+
85+
class SourceAndHeaderFilesWithoutErrors extends SuccessMetric {
86+
SourceAndHeaderFilesWithoutErrors() { this = "source/header files without errors" }
87+
88+
override int getValue() {
89+
result = count(File f | f.fromSource() and not exists(CompilerError e | f = e.getFile()))
90+
}
91+
92+
override SourceAndHeaderFiles getBaseline() { any() }
93+
}
94+
95+
class CompilationsWithoutErrors extends SuccessMetric {
96+
CompilationsWithoutErrors() { this = "compilations without errors" }
97+
98+
override int getValue() {
99+
result = count(Compilation c | not exists(Diagnostic d | d.getFile() = c.getAFileCompiled()))
100+
}
101+
102+
override Compilations getBaseline() { any() }
103+
}
104+
105+
class Expressions extends BaseMetric {
106+
Expressions() { this = "expressions" }
107+
108+
override int getValue() { result = count(Expr e) }
109+
}
110+
111+
class SucceededExpressions extends SuccessMetric {
112+
SucceededExpressions() { this = "non-error expressions" }
113+
114+
override int getValue() { result = count(Expr e) - count(ErrorExpr e) }
115+
116+
override Expressions getBaseline() { any() }
117+
}
118+
119+
class TypedExpressions extends SuccessMetric {
120+
TypedExpressions() { this = "expressions with a known type" }
121+
122+
override int getValue() { result = count(Expr e | not e.getType() instanceof ErroneousType) }
123+
124+
override Expressions getBaseline() { any() }
125+
}
126+
127+
class Calls extends BaseMetric {
128+
Calls() { this = "calls" }
129+
130+
override int getValue() { result = count(Call c) }
131+
}
132+
133+
class CallsWithExplicitTarget extends SuccessMetric {
134+
CallsWithExplicitTarget() { this = "calls with an explicit target" }
135+
136+
override int getValue() {
137+
result = count(Call c | not c.getTarget().getADeclarationEntry().isImplicit())
138+
}
139+
140+
override Calls getBaseline() { any() }
141+
}
142+
143+
class Variables extends BaseMetric {
144+
Variables() { this = "variables" }
145+
146+
override int getValue() { result = count(Variable v) }
147+
}
148+
149+
class VariablesKnownType extends SuccessMetric {
150+
VariablesKnownType() { this = "variables with a known type" }
151+
152+
override int getValue() {
153+
result = count(Variable v | not v.getType() instanceof ErroneousType)
154+
}
155+
156+
override Variables getBaseline() { any() }
157+
}
158+
159+
class LinesOfText extends BaseMetric {
160+
LinesOfText() { this = "lines of text" }
161+
162+
override int getValue() { result = sum(File f | | f.getMetrics().getNumberOfLines()) }
163+
}
164+
165+
class LinesOfCode extends BaseMetric {
166+
LinesOfCode() { this = "lines of code" }
167+
168+
override int getValue() { result = sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) }
169+
}
170+
171+
private predicate errorLine(File file, int line) {
172+
exists(Locatable l, Location loc |
173+
loc = l.getLocation() and
174+
loc.getFile() = file and
175+
line in [loc.getStartLine() .. loc.getEndLine()]
176+
|
177+
l instanceof Diagnostic
178+
or
179+
l instanceof ErrorExpr
180+
)
181+
}
182+
183+
class SucceededLines extends SuccessMetric {
184+
SucceededLines() { this = "lines of code without errors" }
185+
186+
override int getValue() {
187+
result =
188+
sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) -
189+
count(File f, int line | errorLine(f, line))
190+
}
191+
192+
override LinesOfCode getBaseline() { any() }
193+
}
194+
195+
class Functions extends BaseMetric {
196+
Functions() { this = "functions" }
197+
198+
override int getValue() { result = count(Function f) }
199+
}
200+
201+
class SucceededFunctions extends SuccessMetric {
202+
SucceededFunctions() { this = "functions without errors" }
203+
204+
override int getValue() { result = count(Function f | not f.hasErrors()) }
205+
206+
override Functions getBaseline() { any() }
207+
}
208+
209+
class Includes extends BaseMetric {
210+
Includes() { this = "#include directives" }
211+
212+
override int getValue() { result = count(Include i) + count(CannotOpenFileError e) }
213+
}
214+
215+
class SucceededIncludes extends SuccessMetric {
216+
SucceededIncludes() { this = "successfully resolved #include directives" }
217+
218+
override int getValue() { result = count(Include i) }
219+
220+
override Includes getBaseline() { any() }
221+
}
222+
223+
class SucceededIncludeCount extends Metric {
224+
string includeText;
225+
226+
SucceededIncludeCount() {
227+
exists(Include i |
228+
i.getIncludeText() = includeText and
229+
exists(i.getFile().getRelativePath()) // Only report includes from the repo
230+
) and
231+
this = "Successfully included " + includeText
232+
}
233+
234+
int getValue() { result = count(Include i | i.getIncludeText() = includeText) }
235+
236+
string getIncludeText() { result = includeText }
237+
}
238+
239+
class MissingIncludeCount extends Metric {
240+
string includeText;
241+
242+
MissingIncludeCount() {
243+
exists(CannotOpenFileError e | e.getIncludedFile() = includeText) and
244+
this = "Failed to include '" + includeText + "'"
245+
}
246+
247+
int getValue() { result = count(CannotOpenFileError e | e.getIncludedFile() = includeText) }
248+
249+
string getIncludeText() { result = includeText }
250+
}
251+
252+
class CompilerErrors extends ExtractionMetric {
253+
CompilerErrors() { this = "compiler errors" }
254+
255+
override int getValue() { result = count(CompilerError e) }
256+
}
257+
258+
class ErrorCount extends Metric {
259+
ErrorCount() { exists(CompilerError e | e.getMessage() = this) }
260+
261+
int getValue() { result = count(CompilerError e | e.getMessage() = this) }
262+
}
263+
264+
class SyntaxErrorCount extends ExtractionMetric {
265+
SyntaxErrorCount() { this = "syntax errors" }
266+
267+
override int getValue() { result = count(SyntaxError e) }
268+
}
269+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Failed to include header file
3+
* @description A count of all failed includes, grouped by filename.
4+
* @kind metric
5+
* @tags summary telemetry
6+
* @id cpp/telemetry/failed-includes
7+
*/
8+
9+
import Metrics
10+
11+
from CppMetrics::MissingIncludeCount e
12+
where RankMetric<CppMetrics::MissingIncludeCount>::getRank(e) <= 50
13+
select e.getIncludeText(), e.getValue()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @name Successfully included header files
3+
* @description A count of all succeeded includes, grouped by filename.
4+
* @kind metric
5+
* @tags summary telemetry
6+
* @id cpp/telemetry/succeeded-includes
7+
*/
8+
9+
import Metrics
10+
11+
from CppMetrics::SucceededIncludeCount m
12+
where RankMetric<CppMetrics::SucceededIncludeCount>::getRank(m) <= 50
13+
select m.getIncludeText(), m.getValue()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
| 'this' may only be used inside a nonstatic member function | 1 |
2+
| There was an error during this compilation | 1 |
3+
| expected a ')' | 1 |
4+
| expected a ';' | 1 |
5+
| expected an expression | 1 |
6+
| identifier 'no_such_function' is undefined | 1 |
7+
| identifier 'nsf2' is undefined | 1 |
8+
| identifier 'so_is_this' is undefined | 1 |
9+
| identifier 'uint32_t' is undefined | 1 |
10+
| too few arguments in function call | 1 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Telemetry/CompilerErrors.ql
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
| Percentage of calls with an explicit target | 50.0 |
2+
| Percentage of compilations without errors | 50.0 |
3+
| Percentage of expressions with a known type | 30.0 |
4+
| Percentage of functions without errors | 75.0 |
5+
| Percentage of lines of code without errors | 63.1578947368421 |
6+
| Percentage of non-error expressions | 30.0 |
7+
| Percentage of source/header files without errors | 66.66666666666667 |
8+
| Percentage of successfully resolved #include directives | 100.0 |
9+
| Percentage of variables with a known type | 90.0 |

0 commit comments

Comments
 (0)