Skip to content

Commit 6141c96

Browse files
authored
Merge pull request github#12294 from MathiasVP/visit-recursive-evaluation
QL: Add a visitor for traversing recursive evaluations
2 parents 39a30cf + dcc9b66 commit 6141c96

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

ql/ql/src/codeql_ql/StructuredLogs.qll

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ module KindPredicatesLog {
264264

265265
float getResultSize() { result = this.getFloat("resultSize") }
266266

267+
Array getRA(string ordering) { result = this.getObject("ra").getArray(ordering) }
268+
269+
string getAnOrdering() { exists(this.getRA(result)) }
270+
267271
override string toString() {
268272
if exists(this.getPredicateName())
269273
then result = this.getPredicateName()
@@ -325,12 +329,137 @@ module KindPredicatesLog {
325329
Predicate getPredicate() { result = getPredicateFromPosition(this.getPosition()) }
326330
}
327331

332+
/** Gets the `index`'th event that's evaluated by `recursive`. */
333+
private InLayer layerEventRank(ComputeRecursive recursive, int index) {
334+
result =
335+
rank[index + 1](InLayer cand, int startline, string filepath |
336+
cand.getComputeRecursiveEvent() = recursive and
337+
cand.hasLocationInfo(filepath, startline, _, _, _)
338+
|
339+
cand order by filepath, startline
340+
)
341+
}
342+
343+
/**
344+
* Gets the first predicate that's evaluated in an iteration
345+
* of the SCC computation rooted at `recursive`.
346+
*/
347+
private InLayer firstPredicate(ComputeRecursive recursive) {
348+
result = layerEventRank(recursive, 0)
349+
}
350+
351+
/**
352+
* Gets the last predicate that's evaluated in an iteration
353+
* of the SCC computation rooted at `recursive`.
354+
*/
355+
private InLayer lastPredicate(ComputeRecursive recursive) {
356+
exists(int n |
357+
result = layerEventRank(recursive, n) and
358+
not exists(layerEventRank(recursive, n + 1))
359+
)
360+
}
361+
362+
/**
363+
* Holds if the predicate represented by `next` was evaluated after the
364+
* predicate represented by `prev` in the SCC computation rooted at `recursive`.
365+
*/
366+
predicate successor(ComputeRecursive recursive, InLayer prev, InLayer next) {
367+
exists(int index |
368+
layerEventRank(recursive, index) = prev and
369+
layerEventRank(recursive, index + 1) = next
370+
)
371+
}
372+
373+
bindingset[this]
374+
signature class ResultSig;
375+
376+
/**
377+
* A signature for generically traversing a SCC computation.
378+
*/
379+
signature module Fold<ResultSig R> {
380+
/**
381+
* Gets the base case for the fold. That is, the initial value that
382+
* is produced from the first evaluation of the first IN_LAYER event
383+
* in the recursive evaluation.
384+
*/
385+
bindingset[run]
386+
R base(PipeLineRun run);
387+
388+
/**
389+
* Gets the recursive case for the fold. That is, `r` is the accumulation
390+
* of the previous evaluations, and `run` is the pipeline of the next IN_LAYER
391+
* event that is evaluated.
392+
*/
393+
bindingset[run, r]
394+
R fold(PipeLineRun run, R r);
395+
}
396+
397+
module Iterate<ResultSig R, Fold<R> F> {
398+
private R iterate(ComputeRecursive recursive, int iteration, InLayer pred) {
399+
// Case: The first iteration
400+
iteration = 0 and
401+
(
402+
// Subcase: The first predicate in the first iteration
403+
pred = firstPredicate(recursive) and
404+
result = F::base(pred.getPipelineRuns().getRun(0))
405+
or
406+
// Subcase: The predicate has a predecessor
407+
exists(InLayer pred0, R r |
408+
successor(recursive, pred0, pred) and
409+
r = iterate(recursive, 0, pred0) and
410+
result = F::fold(pred.getPipelineRuns().getRun(0), r)
411+
)
412+
)
413+
or
414+
// Case: Not the first iteration
415+
iteration > 0 and
416+
(
417+
// Subcase: The first predicate in the iteration
418+
pred = firstPredicate(recursive) and
419+
exists(InLayer last, R r |
420+
last = lastPredicate(recursive) and
421+
r = iterate(recursive, iteration - 1, last) and
422+
result = F::fold(pred.getPipelineRuns().getRun(iteration), r)
423+
)
424+
or
425+
// Subcase: The predicate has a predecessor in the same iteration
426+
exists(InLayer pred0, R r |
427+
successor(recursive, pred0, pred) and
428+
r = iterate(recursive, iteration, pred0) and
429+
result = F::fold(pred.getPipelineRuns().getRun(iteration), r)
430+
)
431+
)
432+
}
433+
434+
R iterate(ComputeRecursive recursive) {
435+
exists(int iteration, InLayer pred |
436+
pred = lastPredicate(recursive) and
437+
result = iterate(recursive, iteration, pred) and
438+
not exists(iterate(recursive, iteration + 1, pred))
439+
)
440+
}
441+
}
442+
328443
class ComputeRecursive extends SummaryEvent {
329444
ComputeRecursive() { evaluationStrategy = "COMPUTE_RECURSIVE" }
330445
}
331446

332447
class InLayer extends SummaryEvent {
333448
InLayer() { evaluationStrategy = "IN_LAYER" }
449+
450+
string getMainHash() { result = this.getString("mainHash") }
451+
452+
ComputeRecursive getComputeRecursiveEvent() { result.getRAHash() = this.getMainHash() }
453+
454+
Array getPredicateIterationMillis() { result = this.getArray("predicateIterationMillis") }
455+
456+
float getPredicateIterationMillis(int i) {
457+
result = getRanked(this.getArray("predicateIterationMillis"), i)
458+
}
459+
460+
PipeLineRuns getPipelineRuns() { result = this.getArray("pipelineRuns") }
461+
462+
float getDeltaSize(int i) { result = getRanked(this.getArray("deltaSizes"), i) }
334463
}
335464

336465
class ComputedExtensional extends SummaryEvent {

ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,6 +1866,12 @@ module JSON {
18661866
/** Gets the location of this element. */
18671867
final L::Location getLocation() { json_ast_node_info(this, _, _, result) }
18681868

1869+
predicate hasLocationInfo(
1870+
string filepath, int startline, int startcolumn, int endline, int endcolumn
1871+
) {
1872+
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
1873+
}
1874+
18691875
/** Gets the parent of this element. */
18701876
final AstNode getParent() { json_ast_node_info(this, result, _, _) }
18711877

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Shows a list of the "slow" predicates by tuple sum.
3+
*/
4+
5+
import ql
6+
import codeql_ql.StructuredLogs
7+
import KindPredicatesLog
8+
9+
module SumCounts implements Fold<int> {
10+
int base(PipeLineRun run) { result = sum(int i | | run.getCount(i)) }
11+
12+
bindingset[s]
13+
int fold(PipeLineRun run, int s) { result = sum(int i | | run.getCount(i)) + s }
14+
}
15+
16+
int sumTuples(SummaryEvent event) {
17+
result = strictsum(int i | | event.(ComputeSimple).getPipelineRun().getCount(i))
18+
or
19+
result = Iterate<int, SumCounts>::iterate(event)
20+
}
21+
22+
int predicateRank(SummaryEvent evt) {
23+
evt = rank[result](SummaryEvent y, int s | s = sumTuples(y) | y order by s desc)
24+
}
25+
26+
from SummaryEvent evt, int s
27+
where predicateRank(evt) < 50 and s = sumTuples(evt)
28+
select evt, s order by s desc

0 commit comments

Comments
 (0)