Skip to content

Commit 6dbe827

Browse files
author
Esben Sparre Andreasen
committed
JS: add QL classes for the extraction metrics
1 parent 5665cf9 commit 6dbe827

File tree

14 files changed

+257
-0
lines changed

14 files changed

+257
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @name File correlations
3+
* @description
4+
* @kind table
5+
* @id js/meta/extraction/file-data
6+
*/
7+
8+
import javascript
9+
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
10+
11+
FileWithExtractionMetrics getACacheMember(string cacheFile) { cacheFile = result.getCacheFile() }
12+
13+
FileWithExtractionMetrics getACacheHit(FileWithExtractionMetrics f) {
14+
result = getACacheMember(f.getCacheFile()) and
15+
result.isFromCache()
16+
}
17+
18+
from FileWithExtractionMetrics file, boolean fromCache
19+
where (if file.isFromCache() then fromCache = true else fromCache = false)
20+
select file.getAbsolutePath() as FILE, file.getCpuTime() as CPU_NANO,
21+
file.getNumberOfLines() as LINES, count(Locatable n | n.getFile() = file) as LOCATABLES,
22+
count(TypeAnnotation n | n.getFile() = file) as TYPES, file.getLength() as LENGTH,
23+
fromCache as FROM_CACHE, count(getACacheMember(file.getCacheFile())) as CACHE_MEMBERS,
24+
count(getACacheHit(file)) as CACHE_HITS, file.getCacheFile() as CACHE_FILE
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @name File with missing extraction metrics
3+
* @description A file missing extraction metrics is indicative of a faulty extractor.
4+
* @kind table
5+
* @problem.severity error
6+
* @id js/meta/extraction/missing-metrics
7+
*/
8+
9+
import javascript
10+
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
11+
12+
from File f, string cause
13+
where not extraction_data(f, _, _, _) and cause = "No extraction_data for this file"
14+
or
15+
not extraction_time(f, _,_, _) and cause = "No extraction_time for this file"
16+
select f, cause
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @name Extractor phase timings
3+
* @description An overview of how time was spent during extraction
4+
* @kind table
5+
* @id js/meta/extraction/phase-timings
6+
*/
7+
8+
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
9+
10+
from PhaseName phaseName, float cpuTime, int cpuPerc
11+
where
12+
cpuTime = Aggregated::getCpuTime(phaseName) and
13+
cpuPerc = ((cpuTime / Aggregated::getCpuTime()) * 100).floor()
14+
select phaseName, cpuTime as CPU_NANO, cpuPerc as CPU_PERC
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import javascript
2+
3+
/**
4+
* INTERNAL: Do not use in ordinary queries.
5+
*
6+
* Extraction metrics for profiling extraction behaviours.
7+
*/
8+
module ExtractionMetrics {
9+
/**
10+
* A file with extraction metrics.
11+
*/
12+
class FileWithExtractionMetrics extends File {
13+
14+
FileWithExtractionMetrics() { extraction_data(this, _, _, _) and extraction_time(this, _, _, _)}
15+
16+
/**
17+
* Gets the CPU time in nanoseconds it took to extract this file.
18+
*/
19+
float getCpuTime() { result = strictsum(getTime(_, 0)) }
20+
21+
/**
22+
* Gets the wall-clock time in nanoseconds it took to extract this file.
23+
*/
24+
float getWallclockTime() { result = strictsum(getTime(_, 1)) }
25+
26+
/**
27+
* Gets the CPU time in nanoseconds it took to process phase `phaseName` during the extraction this file.
28+
*/
29+
float getCpuTime(PhaseName phaseName) { result = getTime(phaseName, 0) }
30+
31+
/**
32+
* Gets the wall-clock time in nanoseconds it took to process phase `phaseName` during the extraction this file.
33+
*/
34+
float getWallclockTime(PhaseName phaseName) { result = getTime(phaseName, 1) }
35+
36+
/**
37+
* Holds if this file was extracted from the trap cache.
38+
*/
39+
predicate isFromCache() { extraction_data(this, _, true, _) }
40+
41+
/**
42+
* Gets the path to the cache file used for extraction of this file.
43+
*/
44+
string getCacheFile() { extraction_data(this, result, _, _) }
45+
46+
/**
47+
* Gets the number of characters in this file.
48+
*/
49+
int getLength() { extraction_data(this, _, _, result) }
50+
51+
private float getTime(PhaseName phaseName, int timerKind) {
52+
// note that we use strictsum to make it clear if data is missing because it comes from an upgraded database.
53+
strictsum(int phaseId, float r |
54+
phaseName = getExtractionPhaseName(phaseId) and
55+
extraction_time(this, phaseId, timerKind, r)
56+
|
57+
r
58+
) = result
59+
}
60+
}
61+
62+
/**
63+
* Converts database ids to human-readable names.
64+
*/
65+
private string getExtractionPhaseName(int phaseId) {
66+
// these names ought to match the names used in
67+
// `com.semmle.js.extractor.ExtractionTimer.ExtractionPhase`
68+
"ASTExtractor_extract" = result and 0 = phaseId
69+
or
70+
"CFGExtractor_extract" = result and 1 = phaseId
71+
or
72+
"FileExtractor_extractContents" = result and 2 = phaseId
73+
or
74+
"JSExtractor_extract" = result and 3 = phaseId
75+
or
76+
"JSParser_parse" = result and 4 = phaseId
77+
or
78+
"LexicalExtractor_extractLines" = result and 5 = phaseId
79+
or
80+
"LexicalExtractor_extractTokens" = result and 6 = phaseId
81+
or
82+
"TypeScriptASTConverter_convertAST" = result and 7 = phaseId
83+
or
84+
"TypeScriptParser_talkToParserWrapper" = result and 8 = phaseId
85+
}
86+
87+
88+
/**
89+
* The name of a phase of the extraction.
90+
*/
91+
class PhaseName extends string {
92+
bindingset[this]
93+
PhaseName() { this = getExtractionPhaseName(_) }
94+
}
95+
96+
/**
97+
* Utilities for aggregating metrics for multiple files.
98+
*/
99+
module Aggregated {
100+
/**
101+
* Gets the total CPU time spent on extraction.
102+
*/
103+
float getCpuTime() { result = strictsum(any(FileWithExtractionMetrics f).getCpuTime()) }
104+
105+
/**
106+
* Gets the total wallclock time spent on extraction.
107+
*/
108+
float getWallclockTime() { result = strictsum(any(FileWithExtractionMetrics f).getWallclockTime()) }
109+
110+
/**
111+
* Gets the total CPU time spent in phase `phaseName` of the extraction.
112+
*/
113+
float getCpuTime(PhaseName phaseName) {
114+
/* bind */ phaseName = getExtractionPhaseName(_) and
115+
result = strictsum(any(FileWithExtractionMetrics f).getCpuTime(phaseName))
116+
}
117+
118+
/**
119+
* Gets the total wallclock time spent in phase `phaseName` of the extraction.
120+
*/
121+
float getWallclockTime(PhaseName phaseName) {
122+
/* bind */ phaseName = getExtractionPhaseName(_) and
123+
result = strictsum(any(FileWithExtractionMetrics f).getWallclockTime(phaseName))
124+
}
125+
}
126+
127+
/**
128+
* Gets `nanoseconds` formatted as a whole number of milliseconds.
129+
*/
130+
bindingset[nanoSeconds]
131+
string formatAsMilliSeconds(float nanoSeconds) {
132+
result = (nanoSeconds / (1000 * 1000)).ceil() + ""
133+
}
134+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| tst2.js:0:0:0:0 | tst2.js |
2+
| tst.html:0:0:0:0 | tst.html |
3+
| tst.js:0:0:0:0 | tst.js |
4+
| tst.ts:0:0:0:0 | tst.ts |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
2+
3+
from FileWithExtractionMetrics f
4+
select f
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| tst2.js:0:0:0:0 | tst2.js | 26 |
2+
| tst.html:0:0:0:0 | tst.html | 127 |
3+
| tst.js:0:0:0:0 | tst.js | 26 |
4+
| tst.ts:0:0:0:0 | tst.ts | 31 |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
2+
3+
from FileWithExtractionMetrics f
4+
select f, f.getLength()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
| tst2.js:0:0:0:0 | tst2.js | ASTExtractor_extract |
2+
| tst2.js:0:0:0:0 | tst2.js | CFGExtractor_extract |
3+
| tst2.js:0:0:0:0 | tst2.js | FileExtractor_extractContents |
4+
| tst2.js:0:0:0:0 | tst2.js | JSExtractor_extract |
5+
| tst2.js:0:0:0:0 | tst2.js | JSParser_parse |
6+
| tst2.js:0:0:0:0 | tst2.js | LexicalExtractor_extractLines |
7+
| tst2.js:0:0:0:0 | tst2.js | LexicalExtractor_extractTokens |
8+
| tst2.js:0:0:0:0 | tst2.js | TypeScriptASTConverter_convertAST |
9+
| tst2.js:0:0:0:0 | tst2.js | TypeScriptParser_talkToParserWrapper |
10+
| tst.html:0:0:0:0 | tst.html | ASTExtractor_extract |
11+
| tst.html:0:0:0:0 | tst.html | CFGExtractor_extract |
12+
| tst.html:0:0:0:0 | tst.html | FileExtractor_extractContents |
13+
| tst.html:0:0:0:0 | tst.html | JSExtractor_extract |
14+
| tst.html:0:0:0:0 | tst.html | JSParser_parse |
15+
| tst.html:0:0:0:0 | tst.html | LexicalExtractor_extractLines |
16+
| tst.html:0:0:0:0 | tst.html | LexicalExtractor_extractTokens |
17+
| tst.html:0:0:0:0 | tst.html | TypeScriptASTConverter_convertAST |
18+
| tst.html:0:0:0:0 | tst.html | TypeScriptParser_talkToParserWrapper |
19+
| tst.js:0:0:0:0 | tst.js | ASTExtractor_extract |
20+
| tst.js:0:0:0:0 | tst.js | CFGExtractor_extract |
21+
| tst.js:0:0:0:0 | tst.js | FileExtractor_extractContents |
22+
| tst.js:0:0:0:0 | tst.js | JSExtractor_extract |
23+
| tst.js:0:0:0:0 | tst.js | JSParser_parse |
24+
| tst.js:0:0:0:0 | tst.js | LexicalExtractor_extractLines |
25+
| tst.js:0:0:0:0 | tst.js | LexicalExtractor_extractTokens |
26+
| tst.js:0:0:0:0 | tst.js | TypeScriptASTConverter_convertAST |
27+
| tst.js:0:0:0:0 | tst.js | TypeScriptParser_talkToParserWrapper |
28+
| tst.ts:0:0:0:0 | tst.ts | ASTExtractor_extract |
29+
| tst.ts:0:0:0:0 | tst.ts | CFGExtractor_extract |
30+
| tst.ts:0:0:0:0 | tst.ts | FileExtractor_extractContents |
31+
| tst.ts:0:0:0:0 | tst.ts | JSExtractor_extract |
32+
| tst.ts:0:0:0:0 | tst.ts | JSParser_parse |
33+
| tst.ts:0:0:0:0 | tst.ts | LexicalExtractor_extractLines |
34+
| tst.ts:0:0:0:0 | tst.ts | LexicalExtractor_extractTokens |
35+
| tst.ts:0:0:0:0 | tst.ts | TypeScriptASTConverter_convertAST |
36+
| tst.ts:0:0:0:0 | tst.ts | TypeScriptParser_talkToParserWrapper |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics
2+
3+
from FileWithExtractionMetrics f, PhaseName phase
4+
where
5+
exists(f.getCpuTime(phase)) and
6+
exists(f.getWallclockTime(phase))
7+
select f, phase

0 commit comments

Comments
 (0)