Skip to content

Commit f2d1e53

Browse files
authored
Merge pull request github#1914 from jf205/query-debugging
docs: make a start on query debugging topic
2 parents 5ecfaed + ff4a604 commit f2d1e53

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
Query writing: common performance issues
2+
========================================
3+
4+
This topic offers some simple tips on how to avoid common problems that can affect the performance of your queries.
5+
Before reading the tips below, it is worth reiterating a few important points about CodeQL and the QL language:
6+
7+
- CodeQL `predicates <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ and `classes <https://help.semmle.com/QL/ql-handbook/types.html#classes>`__ are evaluated to database `tables <https://en.wikipedia.org/wiki/Table_(database)>`__. Large predicates generate large tables with many rows, and are therefore expensive to compute.
8+
- The QL language is implemented using standard database operations and `relational algebra <https://en.wikipedia.org/wiki/Relational_algebra>`__ (such as join, projection, and union). For further information about query languages and databases, see :doc:`About QL <../about-ql>`.
9+
- Queries are evaluated *bottom-up*, which means that a predicate is not evaluated until *all* of the predicates that it depends on are evaluated. For more information on query evaluation, see `Evaluation of QL programs <https://help.semmle.com/QL/ql-handbook/evaluation.html>`__ in the QL handbook.
10+
11+
Performance tips
12+
----------------
13+
14+
Follow the guidelines below to ensure that you don't get tripped up by the most common CodeQL performance pitfalls.
15+
16+
Eliminate cartesian products
17+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18+
19+
The performance of a predicate can often be judged by considering roughly how many results it has.
20+
One way of creating badly performing predicates is by using two variables without relating them in any way, or only relating them using a negation.
21+
This leads to computing the `Cartesian product <https://en.wikipedia.org/wiki/Cartesian_product>`__ between the sets of possible values for each variable, potentially generating a huge table of results.
22+
23+
This can occur if you don't specify restrictions on your variables.
24+
25+
For instance, consider the following predicate that checks whether a Java method ``m`` may access a field ``f``::
26+
27+
predicate mayAccess(Method m, Field f) {
28+
f.getAnAccess().getEnclosingCallable() = m
29+
or
30+
not exists(m.getBody())
31+
}
32+
33+
The predicate holds if ``m`` contains an access to ``f``, but also conservatively assumes that methods without bodies (for example, native methods) may access *any* field.
34+
35+
However, if ``m`` is a native method, the table computed by ``mayAccess`` will contain a row ``m, f`` for *all* fields ``f`` in the codebase, making it potentially very large.
36+
37+
This example shows a similar mistake in a member predicate::
38+
39+
class Foo extends Class {
40+
...
41+
// BAD! Does not use ‘this’
42+
Method getToString() {
43+
result.getName() = "ToString"
44+
}
45+
...
46+
}
47+
48+
Note that while ``getToString()`` does not declare any parameters, it has two implicit parameters, ``result`` and ``this``, which it fails to relate. Therefore, the table computed by ``getToString()`` contains a row for every combination of ``result`` and ``this``. That is, a row for every combination of a method named ``"ToString"`` and an instance of ``Foo``.
49+
To avoid making this mistake, ``this`` should be restricted in the member predicate ``getToString()`` on the class ``Foo``.
50+
51+
Use specific types
52+
~~~~~~~~~~~~~~~~~~
53+
54+
`Types <https://help.semmle.com/QL/ql-handbook/types.html>`__ provide an upper bound on the size of a relation.
55+
This helps the query optimizer be more effective, so it's generally good to use the most specific types possible. For example::
56+
57+
predicate foo(LoggingCall e)
58+
59+
is preferred over::
60+
61+
predicate foo(Expr e)
62+
63+
From the type context, the query optimizer deduces that some parts of the program are redundant and removes them, or *specializes* them.
64+
65+
Avoid complex recursion
66+
~~~~~~~~~~~~~~~~~~~~~~~
67+
68+
`Recursion <https://help.semmle.com/QL/ql-handbook/recursion.html>`__ is about self-referencing definitions.
69+
It can be extremely powerful as long as it is used appropriately.
70+
On the whole, you should try to make recursive predicates as simple as possible.
71+
That is, you should define a *base case* that allows the predicate to *bottom out*, along with a single *recursive call*::
72+
73+
int depth(Stmt s) {
74+
exists(Callable c | c.getBody() = s | result = 0) // base case
75+
or
76+
result = depth(s.getParent()) + 1 // recursive call
77+
}
78+
79+
.. pull-quote:: Note
80+
81+
The query optimizer has special data structures for dealing with `transitive closures <https://help.semmle.com/QL/ql-handbook/recursion.html#transitive-closures>`__.
82+
If possible, use a transitive closure over a simple recursive predicate, as it is likely to be computed faster.
83+
84+
Further information
85+
-------------------
86+
87+
- Find out more about QL in the `QL language handbook <https://help.semmle.com/QL/ql-handbook/index.html>`__ and `QL language specification <https://help.semmle.com/QL/ql-spec/language.html>`__.

docs/language/learn-ql/writing-queries/writing-queries.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Information for query writers
1515
../intro-to-data-flow
1616
select-statement
1717
../locations
18+
debugging-queries
1819

1920

2021
Visit `Learning CodeQL <https://help.semmle.com/QL/learn-ql/>`__ to find basic information about CodeQL. This includes information about the underlying query language QL, as well as help and advice on writing queries for specific programming languages.

0 commit comments

Comments
 (0)