Skip to content

Commit f6776a4

Browse files
committed
C++: Initial telemetry queries and tests
1 parent fed240a commit f6776a4

32 files changed

+547
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
select m, m.getValue() as c order by c desc
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, m.getValue() order by m

cpp/ql/src/Telemetry/Diagnostics.qll

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import cpp
2+
3+
/**
4+
* A syntax error.
5+
*/
6+
class SyntaxError extends CompilerError {
7+
SyntaxError() { this.getTag().matches("exp_%") }
8+
}
9+
10+
/**
11+
* A cannot open file error.
12+
* Typically this is due to a missing include.
13+
*/
14+
class CannotOpenFile extends CompilerError {
15+
CannotOpenFile() { this.hasTag("cannot_open_file") }
16+
17+
string getIncludedFile() {
18+
result = this.getMessage().regexpCapture("cannot open source file '([^']+)'", 1)
19+
}
20+
}
21+
22+
/**
23+
* An undefined identifier error.
24+
* Currently unused.
25+
*/
26+
class UndefinedIdentifier extends CompilerError {
27+
UndefinedIdentifier() { this.hasTag("undefined_identifier") }
28+
29+
string getIdentifier() {
30+
result = this.getMessage().regexpCapture("identifier '([^']+)' is undefined", 1)
31+
}
32+
}
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, m.getValue() order by m

cpp/ql/src/Telemetry/Metrics.qll

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
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+
/** Gets the value of this metric. */
12+
abstract float getValue();
13+
}
14+
15+
/**
16+
* A metric that we want to report in cpp/telemetry/extraction-metrics
17+
*/
18+
abstract class ExtractionMetric extends Metric {
19+
bindingset[this]
20+
ExtractionMetric() { any() }
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 base_metric;
51+
SuccessMetric relative_metric;
52+
53+
QualityMetric() {
54+
base_metric = relative_metric.getBaseline() and this = "Percentage of " + relative_metric
55+
}
56+
57+
override float getValue() { result = 100 * relative_metric.getValue() / base_metric.getValue() }
58+
}
59+
60+
/** Various metrics we want to report. */
61+
module CppMetrics {
62+
class CompilationUnits extends BaseMetric {
63+
CompilationUnits() { this = "compilation units" }
64+
65+
override float getValue() { result = count(Compilation c) }
66+
}
67+
68+
class SourceFiles extends BaseMetric {
69+
SourceFiles() { this = "source files" }
70+
71+
override float getValue() { result = count(File f | f.fromSource()) }
72+
}
73+
74+
class SourceFilesWithoutErrors extends SuccessMetric {
75+
SourceFilesWithoutErrors() { this = "source files without errors" }
76+
77+
override float getValue() {
78+
result = count(File f | f.fromSource() and not exists(CompilerError e | f = e.getFile()))
79+
}
80+
81+
override SourceFiles getBaseline() { any() }
82+
}
83+
84+
class CompilationUnitsWithoutErrors extends SuccessMetric {
85+
CompilationUnitsWithoutErrors() { this = "compilation units without errors" }
86+
87+
override float getValue() {
88+
result = count(Compilation c | not exists(Diagnostic d | d.getFile() = c.getAFileCompiled()))
89+
}
90+
91+
override CompilationUnits getBaseline() { any() }
92+
}
93+
94+
class Expressions extends BaseMetric {
95+
Expressions() { this = "expressions" }
96+
97+
override float getValue() { result = count(Expr e) }
98+
}
99+
100+
class SucceededExpressions extends SuccessMetric {
101+
SucceededExpressions() { this = "non-error expressions" }
102+
103+
override float getValue() { result = count(Expr e) - count(ErrorExpr e) }
104+
105+
override Expressions getBaseline() { any() }
106+
}
107+
108+
class TypedExpressions extends SuccessMetric {
109+
TypedExpressions() { this = "expressions with a known type" }
110+
111+
override float getValue() { result = count(Expr e | not e.getType() instanceof ErroneousType) }
112+
113+
override Expressions getBaseline() { any() }
114+
}
115+
116+
class Calls extends BaseMetric {
117+
Calls() { this = "calls" }
118+
119+
override float getValue() { result = count(Call c) }
120+
}
121+
122+
class SucceededCalls extends SuccessMetric {
123+
SucceededCalls() { this = "calls with a target" }
124+
125+
override float getValue() {
126+
result = count(Call c | not c.getTarget().getADeclarationEntry().isImplicit())
127+
}
128+
129+
override Calls getBaseline() { any() }
130+
}
131+
132+
class Variables extends BaseMetric {
133+
Variables() { this = "variables" }
134+
135+
override float getValue() { result = count(Variable v) }
136+
}
137+
138+
class VariablesKnownType extends SuccessMetric {
139+
VariablesKnownType() { this = "variables with a known type" }
140+
141+
override float getValue() {
142+
result = count(Variable v | not v.getType() instanceof ErroneousType)
143+
}
144+
145+
override Variables getBaseline() { any() }
146+
}
147+
148+
class LinesOfText extends BaseMetric {
149+
LinesOfText() { this = "lines of text" }
150+
151+
override float getValue() { result = sum(File f | | f.getMetrics().getNumberOfLines()) }
152+
}
153+
154+
class LinesOfCode extends BaseMetric {
155+
LinesOfCode() { this = "lines of code" }
156+
157+
override float getValue() { result = sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) }
158+
}
159+
160+
private predicate errorLine(File file, int line) {
161+
exists(Locatable l, Location loc |
162+
loc = l.getLocation() and
163+
loc.getFile() = file and
164+
line in [loc.getStartLine() .. loc.getEndLine()]
165+
|
166+
l instanceof Diagnostic
167+
or
168+
l instanceof ErrorExpr
169+
)
170+
}
171+
172+
class SucceededLines extends SuccessMetric {
173+
SucceededLines() { this = "lines of code without errors" }
174+
175+
override float getValue() {
176+
result =
177+
sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) -
178+
count(File file, int line | errorLine(file, line))
179+
}
180+
181+
override LinesOfCode getBaseline() { any() }
182+
}
183+
184+
class Functions extends BaseMetric {
185+
Functions() { this = "functions" }
186+
187+
override float getValue() { result = count(Function f) }
188+
}
189+
190+
class SucceededFunctions extends SuccessMetric {
191+
SucceededFunctions() { this = "functions without errors" }
192+
193+
override float getValue() { result = count(Function f | not f.hasErrors()) }
194+
195+
override Functions getBaseline() { any() }
196+
}
197+
198+
class Includes extends BaseMetric {
199+
Includes() { this = "#include directives" }
200+
201+
override float getValue() { result = count(Include i) + count(CannotOpenFile e) }
202+
}
203+
204+
class SucceededIncludes extends SuccessMetric {
205+
SucceededIncludes() { this = "successfully resolved #include directives" }
206+
207+
override float getValue() { result = count(Include i) }
208+
209+
override Includes getBaseline() { any() }
210+
}
211+
212+
class SucceededIncludeCount extends Metric {
213+
string include_text;
214+
215+
SucceededIncludeCount() {
216+
exists(Include i |
217+
i.getIncludeText() = include_text and
218+
exists(i.getFile().getRelativePath()) // Only report includes from the repo
219+
) and
220+
this = "Successfully included " + include_text
221+
}
222+
223+
override float getValue() { result = count(Include i | i.getIncludeText() = include_text) }
224+
225+
string getIncludeText() { result = include_text }
226+
}
227+
228+
class MissingIncludeCount extends Metric {
229+
string include_text;
230+
231+
MissingIncludeCount() {
232+
exists(CannotOpenFile e | e.getIncludedFile() = include_text) and
233+
this = "Failed to include '" + include_text + "'"
234+
}
235+
236+
override float getValue() {
237+
result = count(CannotOpenFile e | e.getIncludedFile() = include_text)
238+
}
239+
240+
string getIncludeText() { result = include_text }
241+
}
242+
243+
class CompilerErrors extends ExtractionMetric {
244+
CompilerErrors() { this = "compiler errors" }
245+
246+
override float getValue() { result = count(CompilerError e) }
247+
}
248+
249+
class ErrorCount extends Metric {
250+
ErrorCount() { exists(CompilerError e | e.getMessage() = this) }
251+
252+
override float getValue() { result = count(CompilerError e | e.getMessage() = this) }
253+
}
254+
255+
class SyntaxErrorCount extends ExtractionMetric {
256+
SyntaxErrorCount() { this = "syntax errors" }
257+
258+
override float getValue() { result = count(SyntaxError e) }
259+
}
260+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
select e.getIncludeText(), e.getValue() as c order by c desc
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
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.0 |
2+
| There was an error during this compilation | 1.0 |
3+
| expected a ')' | 1.0 |
4+
| expected a ';' | 1.0 |
5+
| expected an expression | 1.0 |
6+
| identifier 'no_such_function' is undefined | 1.0 |
7+
| identifier 'nsf2' is undefined | 1.0 |
8+
| identifier 'so_is_this' is undefined | 1.0 |
9+
| identifier 'uint32_t' is undefined | 1.0 |
10+
| too few arguments in function call | 1.0 |
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 a target | 50.0 |
2+
| Percentage of compilation units 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 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)