File: error_object.md

package info (click to toggle)
vulkan-validationlayers 1.4.328.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 49,412 kB
  • sloc: cpp: 615,223; python: 12,115; sh: 24; makefile: 20; xml: 14
file content (107 lines) | stat: -rw-r--r-- 3,869 bytes parent folder | download | duplicates (13)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# Error Object

When we print error messages, we want to include as much useful information as possible, while also not giving anything that would just be noise. This requires at a `LogError` call to have this information available.

In the past, there was a lot of parameters passed around functions only for the sake of being able to print it out in the `LogError` message.  The `ErrorObject` was created as a way to improve this. With a easier way to pass information, it is less error prone to both forgot the information and/or provide bad information

## Single ErrorObject

The `chassis.cpp` holds the single `ErrorObject` reference which is passed to all `PreCallValidate` calls.

## Location

It is very important to know "where" in a function call the error occured, this is where the `Location` object comes in.

inside `chassis.cpp` we generate the starting `Location` for `ErrorObject`. From here inside each function, we can append what we need

```cpp
// example

bool PreCallValidateQueueBindSparse(/*..*/ ErrorObject &error_obj) const {
    // automatically has Func::vkQueueBindSparse
    // note: error_obj.location is used if just want to print the function
    LogError("VUID-A", error_obj.location);

    for (uint32_t i = 0; i < bindInfoCount; ++i) {
        const Location loc = error_obj.location.dot(Field::pBindInfo, i);
        LogError("VUID-B", loc); // i == 3

        for (uint32_t j = 0; j < bufferBindCount; ++j) {
            const Location buffer_loc = loc.dot(Field::pBufferBinds, j);
            LogError("VUID-C", buffer_loc); // j == 2
        }

        if (pNext == VkTimelineSemaphoreSubmitInfo) {
            // use pNext() instead of dot() to print out it was part of a pNext chain
            const Location pnext_loc = loc.pNext(Struct::VkTimelineSemaphoreSubmitInfo, Field::waitSemaphoreValueCount);
            LogError("VUID-D", pnext_loc);
        }
    }
}
```

will produce the following location in the error message

```
[VUID-A] vkQueueBindSparse():
[VUID-B] vkQueueBindSparse(): pBindInfo[3]
[VUID-C] vkQueueBindSparse(): pBindInfo[3].pBufferBinds[2]
[VUID-D] vkQueueBindSparse(): pBindInfo[3].pNext<VkTimelineSemaphoreSubmitInfo>.waitSemaphoreValueCount
```

> We generate the `Func`/`Struct`/`Field` for all possible items from the XML

### Using fields in the  error messages

using the `Location::Fields()` you can print the location, minus the function, as a string

```cpp
const Location loc = error_obj.location.dot(Field::pBindInfo, i); // vkQueueBindSparse(): pBindInfo[3]

// prints "pBindInfo[3]"
LogError(/*..*/, "%s". loc.Fields().c_str());
```

### Limitations

When using the `.dot()` operation, it returns a new copy of `Location`. If you chain two `.dot().dot()` you need to be mindful.

The following will produce a `stack-use-after-scope` runtime error

```cpp
const Location layout_loc = loc.dot(Field::attachment).dot(Field::layout);
LogError(/*..*/, layout_loc, "error");
```

The 2 ways around the are:

```cpp
// Create 2nd variable
const Location attachment_loc = loc.dot(Field::attachment)
const Location layout_loc     = attachment_loc.dot(Field::layout);
LogError(/*..*/, layout_loc, "good");
```

or has been found to be more common

```cpp
// Pass an argument
LogError(/*..*/, loc.dot(Field::attachment).dot(Field::layout), "good");
```

### Using stack and making copies

The original design was to not make copies of `Location` and just have it modify the `ErrorObject::Location` but this would require you to remove items after a function call

```cpp
// example
error_obj.location.add(Struct::VkSomething);
ValidateItem(error_obj);
error_obj.location.remove(Struct::VkSomething);
```

which slowly leads to a LOT more code and becomes very error prone to forget to remove `Location` values. Instead the `dot` operator return a new copy of `Location`.

## LogObjectList

// TODO