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
|