Implement some custom error classes to throw meaningful errors in code generation
I frequently end up picking a weird random python standard lib error class.
We should instead have our own class, or even better a base class and some more specialized derived ones.
I propose PerftoolError
for starters. This can later be made the base class. Opinions?