profiler: Add number of generated expr to profile o/p

The number of EVAL/REDO counts in the profile result
are sometimes difficult to understand. This is mainly due to the
fact that the compiler rewrites expressions and assigns the
same location to each generated expression and the profiler
keys the counters by the location. So users have no idea
that multiple expressions may be contributing to the profile
result for a given line in the policy.

This change attempts to provide more clarity to the profile
output by including the number of generated expressions for
each given expression thereby helping to better understand
the result and also how the evaluation works.

Fixes: #2552

Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
This commit is contained in:
Ashutosh Narkar
2023-04-12 17:45:14 -07:00
parent 23164c9a62
commit 7f2895ddc8
5 changed files with 231 additions and 83 deletions
+30 -1
View File
@@ -131,13 +131,19 @@ func TestEvalWithProfiler(t *testing.T) {
files := map[string]string{
"x.rego": `package x
p = 1`,
p {
a := 1
b := 2
c := 3
x = a + b * c
}`,
}
test.WithTempFS(files, func(path string) {
params := newEvalCommandParams()
params.profile = true
params.profileCriteria = newrepeatedStringFlag([]string{"line"})
params.dataPaths = newrepeatedStringFlag([]string{path})
var buf bytes.Buffer
@@ -156,6 +162,29 @@ p = 1`,
if len(output.Profile) == 0 {
t.Fatal("Expected profile output to be non-empty")
}
expectedNumEval := []int{3, 1, 1, 1, 1}
expectedNumRedo := []int{3, 1, 1, 1, 1}
expectedRow := []int{7, 6, 5, 4, 1}
expectedNumGenExpr := []int{3, 1, 1, 1, 1}
for idx, actualExprStat := range output.Profile {
if actualExprStat.NumEval != expectedNumEval[idx] {
t.Fatalf("Index %v: Expected number of evals %v but got %v", idx, expectedNumEval[idx], actualExprStat.NumEval)
}
if actualExprStat.NumRedo != expectedNumRedo[idx] {
t.Fatalf("Index %v: Expected number of redos %v but got %v", idx, expectedNumRedo[idx], actualExprStat.NumRedo)
}
if actualExprStat.Location.Row != expectedRow[idx] {
t.Fatalf("Index %v: Expected row %v but got %v", idx, expectedRow[idx], actualExprStat.Location.Row)
}
if actualExprStat.NumGenExpr != expectedNumGenExpr[idx] {
t.Fatalf("Index %v: Expected number of generated expressions %v but got %v", idx, expectedNumGenExpr[idx], actualExprStat.NumGenExpr)
}
}
})
}
+102 -61
View File
@@ -387,24 +387,65 @@ why policy evaluation is slow.
The `opa eval` command provides the following profiler options:
| Option | Detail | Default |
| --- | --- | --- |
| <span class="opa-keep-it-together">`--profile`</span> | Enables expression profiling and outputs profiler results. | off |
| <span class="opa-keep-it-together">`--profile-sort`</span> | Criteria to sort the expression profiling results. This options implies `--profile`. | total_time_ns => num_eval => num_redo => file => line |
| <span class="opa-keep-it-together">`--profile-limit`</span> | Desired number of profiling results sorted on the given criteria. This options implies `--profile`. | 10 |
| <span class="opa-keep-it-together">`--count`</span> | Desired number of evaluations that profiling metrics are to be captured for. With `--format=pretty`, the output will contain min, max, mean and the 90th and 99th percentile. All collected percentiles can be found in the JSON output. | 1 |
| Option | Detail | Default |
| --- | --- |-----------------------------------------------------------------------|
| <span class="opa-keep-it-together">`--profile`</span> | Enables expression profiling and outputs profiler results. | off |
| <span class="opa-keep-it-together">`--profile-sort`</span> | Criteria to sort the expression profiling results. This options implies `--profile`. | total_time_ns => num_eval => num_redo => num_gen_expr => file => line |
| <span class="opa-keep-it-together">`--profile-limit`</span> | Desired number of profiling results sorted on the given criteria. This options implies `--profile`. | 10 |
| <span class="opa-keep-it-together">`--count`</span> | Desired number of evaluations that profiling metrics are to be captured for. With `--format=pretty`, the output will contain min, max, mean and the 90th and 99th percentile. All collected percentiles can be found in the JSON output. | 1 |
#### Sort criteria for the profile results
* `total_time_ns` - Results are displayed is decreasing order of *expression evaluation time*
* `num_eval` - Results are displayed is decreasing order of *number of times an expression is evaluated*
* `num_redo` - Results are displayed is decreasing order of *number of times an expression is re-evaluated(redo)*
* `num_gen_expr` - Results are displayed is decreasing order of *number of generated expressions*
* `file` - Results are sorted in reverse alphabetical order based on the *rego source filename*
* `line` - Results are displayed is decreasing order of *expression line number* in the source file
When the sort criteria is not provided `total_time_ns` has the **highest** priority
while `line` has the **lowest**.
The `num_gen_expr` represents the number of expressions generated for a given statement on a particular line. For example,
let's take the following policy:
```rego
package test
p {
a := 1
b := 2
c := 3
x = a + b * c
}
```
If we profile the above policy we would get something like the following output:
```ruby
+----------+----------+----------+--------------+-------------+
| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION |
+----------+----------+----------+--------------+-------------+
| 20.291µs | 3 | 3 | 3 | test.rego:7 |
| 1µs | 1 | 1 | 1 | test.rego:6 |
| 2.333µs | 1 | 1 | 1 | test.rego:5 |
| 6.333µs | 1 | 1 | 1 | test.rego:4 |
| 84.75µs | 1 | 1 | 1 | data |
+----------+----------+----------+--------------+-------------+
```
The first entry indicates that line `test.rego:7` has a `EVAL/REDO` count of `3`. If we look at the expression on line `test.rego:7`
ie `x = a + b * c` it's not immediately clear why this line has a `EVAL/REDO` count of `3`. But we also notice that there
are `3` generated expressions (ie. `NUM GEN EXPR`) at line `test.rego:7`. This is because the compiler rewrites the above policy to
something like below:
`p = true { __local0__ = 1; __local1__ = 2; __local2__ = 3; mul(__local1__, __local2__, __local3__); plus(__local0__, __local3__, __local4__); x = __local4__ }`
And that line `test.rego:7` is rewritten to `mul(__local1__, __local2__, __local3__); plus(__local0__, __local3__, __local4__); x = __local4__` which
results in a `NUM GEN EXPR` count of `3`. Hence the `NUM GEN EXPR` count can help to better understand the `EVAL/REDO` counts
for a given expression and also provide more clarity into the profile results and how policy evaluation works.
#### Example Policy
The different profiling examples shown later on this page use the below
@@ -483,20 +524,20 @@ opa eval --data rbac.rego --profile --format=pretty 'data.rbac.allow'
```ruby
false
+----------+----------+----------+-----------------+
| TIME | NUM EVAL | NUM REDO | LOCATION |
+----------+----------+----------+-----------------+
| 47.148µs | 1 | 1 | data.rbac.allow |
| 28.965µs | 1 | 1 | rbac.rego:11 |
| 24.384µs | 1 | 1 | rbac.rego:41 |
| 23.064µs | 2 | 1 | rbac.rego:47 |
| 15.525µs | 1 | 1 | rbac.rego:38 |
| 14.137µs | 1 | 2 | rbac.rego:46 |
| 13.927µs | 1 | 0 | rbac.rego:42 |
| 13.568µs | 1 | 1 | rbac.rego:55 |
| 12.982µs | 1 | 0 | rbac.rego:56 |
| 12.763µs | 1 | 2 | rbac.rego:52 |
+----------+----------+----------+-----------------+
+----------+----------+----------+--------------+-----------------+
| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION |
+----------+----------+----------+--------------+-----------------+
| 47.148µs | 1 | 1 | 1 | data.rbac.allow |
| 28.965µs | 1 | 1 | 1 | rbac.rego:11 |
| 24.384µs | 1 | 1 | 1 | rbac.rego:41 |
| 23.064µs | 2 | 1 | 1 | rbac.rego:47 |
| 15.525µs | 1 | 1 | 1 | rbac.rego:38 |
| 14.137µs | 1 | 2 | 1 | rbac.rego:46 |
| 13.927µs | 1 | 0 | 1 | rbac.rego:42 |
| 13.568µs | 1 | 1 | 1 | rbac.rego:55 |
| 12.982µs | 1 | 0 | 1 | rbac.rego:56 |
| 12.763µs | 1 | 2 | 1 | rbac.rego:52 |
+----------+----------+----------+--------------+-----------------+
+------------------------------+----------+
| METRIC | VALUE |
@@ -532,20 +573,20 @@ false
| timer_rego_query_eval_ns | 161812 | 1198092 | 637754 | 1.1846622e+06 | 1.198092e+06 |
| timer_rego_query_parse_ns | 6078 | 6078 | 6078 | 6078 | 6078 |
+------------------------------+---------+----------+---------------+----------------+---------------+
+----------+-------------+-------------+-------------+-------------+----------+----------+-----------------+
| MIN | MAX | MEAN | 90% | 99% | NUM EVAL | NUM REDO | LOCATION |
+----------+-------------+-------------+-------------+-------------+----------+----------+-----------------+
| 43.875µs | 26.135469ms | 11.494512ms | 25.746215ms | 26.135469ms | 1 | 1 | data.rbac.allow |
| 21.478µs | 211.461µs | 98.102µs | 205.72µs | 211.461µs | 1 | 1 | rbac.rego:13 |
| 19.652µs | 123.537µs | 73.161µs | 122.75µs | 123.537µs | 1 | 1 | rbac.rego:40 |
| 12.303µs | 117.277µs | 61.59µs | 116.733µs | 117.277µs | 2 | 1 | rbac.rego:50 |
| 12.224µs | 93.214µs | 51.289µs | 92.217µs | 93.214µs | 1 | 1 | rbac.rego:44 |
| 5.561µs | 84.121µs | 43.002µs | 83.469µs | 84.121µs | 1 | 1 | rbac.rego:51 |
| 5.56µs | 71.712µs | 36.545µs | 71.158µs | 71.712µs | 1 | 0 | rbac.rego:45 |
| 4.958µs | 66.04µs | 33.161µs | 65.636µs | 66.04µs | 1 | 2 | rbac.rego:49 |
| 4.326µs | 65.836µs | 30.461µs | 65.083µs | 65.836µs | 1 | 1 | rbac.rego:6 |
| 3.948µs | 43.399µs | 24.167µs | 43.055µs | 43.399µs | 1 | 2 | rbac.rego:55 |
+----------+-------------+-------------+-------------+-------------+----------+----------+-----------------+
+----------+-------------+-------------+-------------+-------------+----------+----------+--------------+------------------+
| MIN | MAX | MEAN | 90% | 99% | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION |
+----------+-------------+-------------+-------------+-------------+----------+----------+--------------+------------------+
| 43.875µs | 26.135469ms | 11.494512ms | 25.746215ms | 26.135469ms | 1 | 1 | 1 | data.rbac.allow |
| 21.478µs | 211.461µs | 98.102µs | 205.72µs | 211.461µs | 1 | 1 | 1 | rbac.rego:13 |
| 19.652µs | 123.537µs | 73.161µs | 122.75µs | 123.537µs | 1 | 1 | 1 | rbac.rego:40 |
| 12.303µs | 117.277µs | 61.59µs | 116.733µs | 117.277µs | 2 | 1 | 1 | rbac.rego:50 |
| 12.224µs | 93.214µs | 51.289µs | 92.217µs | 93.214µs | 1 | 1 | 1 | rbac.rego:44 |
| 5.561µs | 84.121µs | 43.002µs | 83.469µs | 84.121µs | 1 | 1 | 1 | rbac.rego:51 |
| 5.56µs | 71.712µs | 36.545µs | 71.158µs | 71.712µs | 1 | 0 | 1 | rbac.rego:45 |
| 4.958µs | 66.04µs | 33.161µs | 65.636µs | 66.04µs | 1 | 2 | 1 | rbac.rego:49 |
| 4.326µs | 65.836µs | 30.461µs | 65.083µs | 65.836µs | 1 | 1 | 1 | rbac.rego:6 |
| 3.948µs | 43.399µs | 24.167µs | 43.055µs | 43.399µs | 1 | 2 | 1 | rbac.rego:55 |
+----------+-------------+-------------+-------------+-------------+----------+----------+--------------+------------------+
```
##### Example: Display top `5` profile results
@@ -557,15 +598,15 @@ opa eval --data rbac.rego --profile-limit 5 --format=pretty 'data.rbac.allow'
**Sample Output**
```ruby
+----------+----------+----------+-----------------+
| TIME | NUM EVAL | NUM REDO | LOCATION |
+----------+----------+----------+-----------------+
| 46.329µs | 1 | 1 | data.rbac.allow |
| 26.656µs | 1 | 1 | rbac.rego:11 |
| 24.206µs | 2 | 1 | rbac.rego:47 |
| 23.235µs | 1 | 1 | rbac.rego:41 |
| 18.242µs | 1 | 1 | rbac.rego:38 |
+----------+----------+----------+-----------------+
+----------+----------+----------+--------------+-----------------+
| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION |
+----------+----------+----------+--------------+-----------------+
| 46.329µs | 1 | 1 | 1 | data.rbac.allow |
| 26.656µs | 1 | 1 | 1 | rbac.rego:11 |
| 24.206µs | 2 | 1 | 1 | rbac.rego:47 |
| 23.235µs | 1 | 1 | 1 | rbac.rego:41 |
| 18.242µs | 1 | 1 | 1 | rbac.rego:38 |
+----------+----------+----------+--------------+-----------------+
```
The profile results are sorted on the default sort criteria.
@@ -580,15 +621,15 @@ opa eval --data rbac.rego --profile-limit 5 --profile-sort num_eval --format=pr
**Sample Profile Output**
```ruby
+----------+----------+----------+-----------------+
| TIME | NUM EVAL | NUM REDO | LOCATION |
+----------+----------+----------+-----------------+
| 26.675µs | 2 | 1 | rbac.rego:47 |
| 9.274µs | 2 | 1 | rbac.rego:53 |
| 43.356µs | 1 | 1 | data.rbac.allow |
| 22.467µs | 1 | 1 | rbac.rego:41 |
| 22.425µs | 1 | 1 | rbac.rego:11 |
+----------+----------+----------+-----------------+
+----------+----------+----------+--------------+-----------------+
| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION |
+----------+----------+----------+--------------+-----------------+
| 26.675µs | 2 | 1 | 1 | rbac.rego:47 |
| 9.274µs | 2 | 1 | 1 | rbac.rego:53 |
| 43.356µs | 1 | 1 | 1 | data.rbac.allow |
| 22.467µs | 1 | 1 | 1 | rbac.rego:41 |
| 22.425µs | 1 | 1 | 1 | rbac.rego:11 |
+----------+----------+----------+--------------+-----------------+
```
As seen from the above table, the results are arranged first in decreasing
@@ -606,15 +647,15 @@ opa eval --data rbac.rego --profile-limit 5 --profile-sort num_eval,num_redo --f
**Sample Profile Output**
```ruby
+----------+----------+----------+-----------------+
| TIME | NUM EVAL | NUM REDO | LOCATION |
+----------+----------+----------+-----------------+
| 22.892µs | 2 | 1 | rbac.rego:47 |
| 8.831µs | 2 | 1 | rbac.rego:53 |
| 13.767µs | 1 | 2 | rbac.rego:46 |
| 10.78µs | 1 | 2 | rbac.rego:52 |
| 42.338µs | 1 | 1 | data.rbac.allow |
+----------+----------+----------+-----------------+
+----------+----------+----------+--------------+-----------------+
| TIME | NUM EVAL | NUM REDO | NUM GEN EXPR | LOCATION |
+----------+----------+----------+--------------+-----------------+
| 22.892µs | 2 | 1 | 1 | rbac.rego:47 |
| 8.831µs | 2 | 1 | 1 | rbac.rego:53 |
| 13.767µs | 1 | 2 | 1 | rbac.rego:46 |
| 10.78µs | 1 | 2 | 1 | rbac.rego:52 |
| 42.338µs | 1 | 1 | 1 | data.rbac.allow |
+----------+----------+----------+--------------+-----------------+
```
As seen from the above table, result are first arranged based on *number of evaluations*,
+7 -4
View File
@@ -496,14 +496,16 @@ func prettyAggregatedMetrics(w io.Writer, ms map[string]interface{}, limit int)
func prettyProfile(w io.Writer, profile []profiler.ExprStats) error {
tableProfile := generateTableProfile(w)
for _, rs := range profile {
line := []string{}
timeNs := time.Duration(rs.ExprTimeNs) * time.Nanosecond
timeNsStr := timeNs.String()
numEval := strconv.FormatInt(int64(rs.NumEval), 10)
numRedo := strconv.FormatInt(int64(rs.NumRedo), 10)
numGenExpr := strconv.FormatInt(int64(rs.NumGenExpr), 10)
loc := rs.Location.String()
line = append(line, timeNsStr, numEval, numRedo, loc)
line = append(line, timeNsStr, numEval, numRedo, numGenExpr, loc)
tableProfile.Append(line)
}
if tableProfile.NumLines() > 0 {
@@ -513,7 +515,7 @@ func prettyProfile(w io.Writer, profile []profiler.ExprStats) error {
}
func prettyAggregatedProfile(w io.Writer, profile []profiler.ExprStatsAggregated) error {
tableProfile := generateTableWithKeys(w, append(statKeys, "num eval", "num redo", "location")...)
tableProfile := generateTableWithKeys(w, append(statKeys, "num eval", "num redo", "num gen expr", "location")...)
for _, rs := range profile {
line := []string{}
for _, k := range statKeys {
@@ -526,8 +528,9 @@ func prettyAggregatedProfile(w io.Writer, profile []profiler.ExprStatsAggregated
}
numEval := strconv.FormatInt(int64(rs.NumEval), 10)
numRedo := strconv.FormatInt(int64(rs.NumRedo), 10)
numGenExpr := strconv.FormatInt(int64(rs.NumGenExpr), 10)
loc := rs.Location.String()
line = append(line, numEval, numRedo, loc)
line = append(line, numEval, numRedo, numGenExpr, loc)
tableProfile.Append(line)
}
if tableProfile.NumLines() > 0 {
@@ -611,7 +614,7 @@ func generateTableWithKeys(writer io.Writer, keys ...string) *tablewriter.Table
}
func generateTableProfile(writer io.Writer) *tablewriter.Table {
return generateTableWithKeys(writer, "Time", "Num Eval", "Num Redo", "Location")
return generateTableWithKeys(writer, "Time", "Num Eval", "Num Redo", "Num Gen Expr", "Location")
}
func populateTableMetrics(m metrics.Metrics, table *tablewriter.Table, prettyLimit int) {
+68 -10
View File
@@ -16,13 +16,15 @@ import (
// Profiler computes and reports on the time spent on expressions.
type Profiler struct {
hits map[string]map[int]ExprStats
activeTimer time.Time
prevExpr exprInfo
hits map[string]map[int]ExprStats
hitsByExprIndex map[string]map[int]map[int]ExprStats
activeTimer time.Time
prevExpr exprInfo
}
// exprInfo stores information about an expression.
type exprInfo struct {
index int
location *ast.Location
op topdown.Op
}
@@ -30,7 +32,8 @@ type exprInfo struct {
// New returns a new Profiler object.
func New() *Profiler {
return &Profiler{
hits: map[string]map[int]ExprStats{},
hits: map[string]map[int]ExprStats{},
hitsByExprIndex: map[string]map[int]map[int]ExprStats{},
}
}
@@ -52,9 +55,13 @@ func (p *Profiler) ReportByFile() Report {
p.processLastExpr()
report := Report{Files: map[string]*FileReport{}}
for file, hits := range p.hits {
stats := []ExprStats{}
for _, stat := range hits {
for row, stat := range hits {
if entry, ok := p.hitsByExprIndex[file][row]; ok {
stat.NumGenExpr = len(entry)
}
stats = append(stats, stat)
}
@@ -66,6 +73,7 @@ func (p *Profiler) ReportByFile() Report {
}
fr.Result = stats
}
return report
}
@@ -73,10 +81,14 @@ func (p *Profiler) ReportByFile() Report {
// criteria. If N <= 0, all the results based on the criteria are returned.
func (p *Profiler) ReportTopNResults(numResults int, criteria []string) []ExprStats {
p.processLastExpr()
stats := []ExprStats{}
for _, hits := range p.hits {
for _, stat := range hits {
for file, hits := range p.hits {
for row, stat := range hits {
if entry, ok := p.hitsByExprIndex[file][row]; ok {
stat.NumGenExpr = len(entry)
}
stats = append(stats, stat)
}
}
@@ -92,6 +104,9 @@ func (p *Profiler) ReportTopNResults(numResults int, criteria []string) []ExprSt
allowedCriteria["num_redo"] = func(stat1, stat2 *ExprStats) bool {
return stat1.NumRedo > stat2.NumRedo
}
allowedCriteria["num_gen_expr"] = func(stat1, stat2 *ExprStats) bool {
return stat1.NumGenExpr > stat2.NumGenExpr
}
allowedCriteria["file"] = func(stat1, stat2 *ExprStats) bool {
return stat1.Location.File > stat2.Location.File
}
@@ -156,11 +171,14 @@ func (p *Profiler) processExpr(expr *ast.Expr, eventType topdown.Op) {
p.prevExpr = exprInfo{
op: eventType,
location: expr.Location,
index: expr.Index,
}
return
}
// record the profiler results for the previous expression
p.calculateHitsByExprIndex()
file := p.prevExpr.location.File
hits, ok := p.hits[file]
if !ok {
@@ -190,16 +208,53 @@ func (p *Profiler) processExpr(expr *ast.Expr, eventType topdown.Op) {
p.prevExpr = exprInfo{
op: eventType,
location: expr.Location,
index: expr.Index,
}
}
func (p *Profiler) processLastExpr() {
expr := ast.Expr{
Location: p.prevExpr.location,
Index: p.prevExpr.index,
}
p.processExpr(&expr, p.prevExpr.op)
}
func (p *Profiler) calculateHitsByExprIndex() {
file := p.prevExpr.location.File
hitsUnique, ok := p.hitsByExprIndex[file]
if !ok {
hitsUnique = map[int]map[int]ExprStats{}
hitsUnique[p.prevExpr.location.Row] = map[int]ExprStats{p.prevExpr.index: getProfilerStats(p.prevExpr, p.activeTimer)}
p.hitsByExprIndex[file] = hitsUnique
} else {
row := p.prevExpr.location.Row
idx := p.prevExpr.index
pStats, ok := hitsUnique[row]
if !ok {
hitsUnique[row] = map[int]ExprStats{idx: getProfilerStats(p.prevExpr, p.activeTimer)}
} else {
pStatsIdx, ok := pStats[idx]
if !ok {
hitsUnique[row][idx] = getProfilerStats(p.prevExpr, p.activeTimer)
} else {
pStatsIdx.ExprTimeNs += time.Since(p.activeTimer).Nanoseconds()
switch p.prevExpr.op {
case topdown.EvalOp:
pStatsIdx.NumEval++
case topdown.RedoOp:
pStatsIdx.NumRedo++
}
hitsUnique[row][idx] = pStatsIdx
}
}
}
}
func getProfilerStats(expr exprInfo, timer time.Time) ExprStats {
profilerStats := ExprStats{}
profilerStats.ExprTimeNs = time.Since(timer).Nanoseconds()
@@ -219,6 +274,7 @@ type ExprStats struct {
ExprTimeNs int64 `json:"total_time_ns"`
NumEval int `json:"num_eval"`
NumRedo int `json:"num_redo"`
NumGenExpr int `json:"num_gen_expr"`
Location *ast.Location `json:"location"`
}
@@ -228,6 +284,7 @@ type ExprStatsAggregated struct {
ExprTimeNsStats interface{} `json:"total_time_ns_stats"`
NumEval int `json:"num_eval"`
NumRedo int `json:"num_redo"`
NumGenExpr int `json:"num_gen_expr"`
Location *ast.Location `json:"location"`
}
@@ -236,9 +293,10 @@ func aggregate(stats ...ExprStats) ExprStatsAggregated {
return ExprStatsAggregated{}
}
res := ExprStatsAggregated{
NumEval: stats[0].NumEval,
NumRedo: stats[0].NumRedo,
Location: stats[0].Location,
NumEval: stats[0].NumEval,
NumRedo: stats[0].NumRedo,
NumGenExpr: stats[0].NumGenExpr,
Location: stats[0].Location,
}
timeNs := make([]int64, 0, len(stats))
for _, s := range stats {
+24 -7
View File
@@ -23,6 +23,7 @@ func TestProfilerLargeArray(t *testing.T) {
module := `package test
foo {
p
bar
not baz
bee
@@ -44,7 +45,15 @@ baz {
true
false
true
}`
}
p {
a := 1
b := 2
c := 3
x = a + b * c
}
`
_, err := ast.ParseModule("test.rego", module)
if err != nil {
@@ -71,13 +80,14 @@ baz {
t.Fatal("Expected file report for test.rego")
}
if len(fr.Result) != 11 {
t.Fatalf("Expected file report length to be 11 instead got %v", len(fr.Result))
if len(fr.Result) != 16 {
t.Fatalf("Expected file report length to be 16 instead got %v", len(fr.Result))
}
expectedNumEval := []int{1, 2, 1, 1, 1, 1633, 1, 1, 1, 1, 1}
expectedNumRedo := []int{1, 0, 0, 1, 1633, 0, 1, 1, 1, 1, 0}
expectedRow := []int{4, 5, 6, 10, 11, 12, 16, 17, 18, 22, 23}
expectedNumEval := []int{1, 1, 2, 1, 1, 1, 1633, 1, 1, 1, 1, 1, 1, 1, 1, 3}
expectedNumRedo := []int{1, 1, 0, 0, 1, 1633, 0, 1, 1, 1, 1, 0, 1, 1, 1, 3}
expectedRow := []int{4, 5, 6, 7, 11, 12, 13, 17, 18, 19, 23, 24, 29, 30, 31, 32}
expectedNumGenExpr := []int{1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3}
for idx, actualExprStat := range fr.Result {
if actualExprStat.NumEval != expectedNumEval[idx] {
@@ -92,6 +102,9 @@ baz {
t.Fatalf("Index %v: Expected row %v but got %v", idx, expectedRow[idx], actualExprStat.Location.Row)
}
if actualExprStat.NumGenExpr != expectedNumGenExpr[idx] {
t.Fatalf("Index %v: Expected number of generated expressions %v but got %v", idx, expectedNumGenExpr[idx], actualExprStat.NumGenExpr)
}
}
}
@@ -464,6 +477,7 @@ allowed_operations = [
expectedNumEval := []int{2, 1}
expectedNumRedo := []int{2, 1}
expectedNumGenExpr := []int{1, 1}
expectedLocation := []string{"???", "data.partial.__result__"}
for idx, actualExprStat := range fr.Result {
@@ -475,10 +489,13 @@ allowed_operations = [
t.Fatalf("Index %v: Expected number of redos %v but got %v", idx, expectedNumRedo[idx], actualExprStat.NumRedo)
}
if actualExprStat.NumGenExpr != expectedNumGenExpr[idx] {
t.Fatalf("Index %v: Expected number of generated expressions %v but got %v", idx, expectedNumGenExpr[idx], actualExprStat.NumGenExpr)
}
if string(actualExprStat.Location.Text) != expectedLocation[idx] {
t.Fatalf("Index %v: Expected location %v but got %v", idx, expectedLocation[idx], string(actualExprStat.Location.Text))
}
}
}