-
Notifications
You must be signed in to change notification settings - Fork 84
Rework system outputting warnings #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
for now with just the unknown type and still the mywarn name
sim642
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In #201 (comment) it was said:
- Reports and warnings should be merged with this new approach.
Yet Messages.report is still a thing.
src/util/defaults.ml
Outdated
| ; reg Debugging "dbg.warn.behavior" "true" "undefined behavior warnings" | ||
| ; reg Debugging "dbg.warn.integer" "true" "integer (Overflow, Div_by_zero) warnings" | ||
| ; reg Debugging "dbg.warn.cast" "true" "Cast (Type_mismatch(bug) warnings" | ||
| ; reg Debugging "dbg.warn.race" "true" "Race warnings" | ||
| ; reg Debugging "dbg.warn.array" "true" "Array (Out_of_bounds of int*int) warnings" | ||
| ; reg Debugging "dbg.warn.unknown" "true" "Unknown (of string) warnings" | ||
| ; reg Debugging "dbg.warn.debug" "true" "Debug (of string) warnings" | ||
| ; reg Debugging "dbg.warn.may" "true" "Enable or disable may warnings" | ||
| ; reg Debugging "dbg.warn.must" "true" "Enable or disable must warnings" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably makes sense to have a separate category for warnings instead of shoving them all under debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is now a separate category, but unfortunately just warn is already used in Std so I named the option warn_filter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warn should not be needed as often, so it could be renamed to warn_at or sth similar which is more descriptive anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alright, I renamed warn to warn_at and put all the options for our warnings into just warn instead of the warn_filter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to me like the options could even be warn.at and warn.filter also in the warning options category.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe warn.on.x since warn.filter.x can be confused as filter out x or only show x.
src/util/messages.ml
Outdated
| | Must -> "[MUST]" | ||
| end | ||
|
|
||
| module LogEvent = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this called LogEvent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would be happy to change it, if you have a different suggestion ? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are any of those called Event? IMO sth like Warn.Integer.Overflow would be more readable than M.EventType (M.EventType.Integer M.IntegerEvent.Overflow).
Of course if you want to filter Integer as a group, it can't be flat but you need some run-time representation. Could define some shortcuts or even generate them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also see #201 (comment)
I would also nest those modules to reflect the hierarchy of the types. But then you have problems with the different paths w/o shortcuts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this should be called Warning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Warning or Warn sound good 👍
I originally named it LogEvent since I associate warn, debug etc with logging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay I changed LogEvent to Warning, EventType to WarnType and the others to the version without Event too - such as IntegerEvent to Integer. Is it okay like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I felt like the combination of WarnType and Warning was a little confusing after all and changed it to Warning and WarningWithCertainty. The warn and warn_each functions now accept Warning and an optional bool parameter must. Then the WarningWithCertainty is constructed from that internally. So the users should never have to type this long module name and only use the Warning module.
src/analyses/base.ml
Outdated
| let warn_and_top_on_zero x = | ||
| if GU.opt_predicate (BI.equal BI.zero) (ID.to_int x) then | ||
| (M.warn "Must Undefined Behavior: Second argument of div or mod is 0, continuing with top"; | ||
| (M.warn (M.LogEvent.may (M.EventType.Unknown ("Must Undefined Behavior: Second argument of div or mod is 0, continuing with top"))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of defining Certainty.Must and BehaviorEvent if they're not properly used? Same goes for many other EventTypes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have currently only changed our newly added warnings to their proper types. These changes with the unknowns are for backwards compatibility to make it compile as we were not sure what exact types should be which of these lines. This line you commented on is especially tricky since the text says Undefined behavior but I think in the categories it should be Integer.DivByZero ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should be
Integer.DivByZero
Which is undefined behavior (same goes for Integer.Overflow).
Those Event modules need some cleanup/reordering.
If each of those modules just consists of a type t and show for it, you could also just put all types in one module and name the types accordingly.
src/util/messages.ml
Outdated
| module IntegerEvent = | ||
| struct | ||
| type t = Overflow | DivByZero | ||
| end | ||
|
|
||
| module CastEvent = | ||
| struct | ||
| type t = TypeMismatch | ||
| end | ||
|
|
||
| module ArrayEvent = | ||
| struct | ||
| type t = OutOfBounds of int*int | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's quite clear that these aren't properly used if they don't have show implemented and everything is still fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, these are just prepared here to be extended as we weren't exactly sure where to use them or what information would be relevant to pass into them. Plus for example, we now have Array out of bounds under Undefined behavior and I actually don't know which one should it be - whether array out of bounds should be in this Array event or whether it should belong under the undefined behavior which it also is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put it in undefined behavior. Most of our warnings prob. are about UB. Which ones aren't besides asserts?
If we had some warnings in Array and UB.Array, it'd be cumbersome to filter for both.
If we have such cases, we could let UB.Array imply Array for filtering.
| end | ||
|
|
||
| module Certainty = struct | ||
| type t = May | Must |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know where, but somewhere we must it very clear that this Must doesn't truly mean "must" as commonly understood in program analysis. Not only does this have to be clear to every Goblint developer in the code, but somehow also to any user just running Goblint and reading the output.
Maybe it just needs a new name like MustIfReach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed putting this disclaimer into the readme or the docs but a new name as suggested sounds like the best solution to us. We'll do that, thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't MustIfReach imply that whenever a line is reached then this event occurs? Whereas in the following example the line with the array access is reached multiple times but only once it's a Must out of bounds access.
void f(int n) {
int arr[5];
arr[n] = 1; // this line is reached 3 times and in one case it's in bounds, in one certainly out of bounds and in the last one it could be anything
}
int main() {
f(3); // -> this results in an in bounds access
f(10); // -> this results in a Must Warning in the function f
int top;
f(top); // -> this results in a May Warning in the function f
}
What I'd like to see here ideally would be something like
arr[n] = 1; is out of bounds following the function call f(10);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there's dbg.warn_with_context which is off by default and currently only supplied the context for asserts.
If you turn it on and add ~ctx, the warning should include the context of f(10).
Ideally, the result viewer would then take care to indicate such things.
src/analyses/region.ml
Outdated
| let to_exp (v,f) = (v,Lval.Fields.to_offs' f) in | ||
| List.map to_exp (Reg.related_globals ev (part,reg)) | ||
| | `Top -> Messages.warn "Region state is broken :("; [] | ||
| | `Top -> Messages.warn (Messages.LogEvent.may (Messages.EventType.Unknown ("Region state is broken :("))); [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems too verbose. Not specific to this, but in general.
Now with the non-polymorphic variants, there's no way around the path, but maybe change all those calls to
Messages.(warn (LogEvent.may (EventType.Unknown ("Region state is broken :("))))?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in most modules Messages was imported as just M. Would maybe some similar shortcuts help with the verbosity too? Or bring some more of the types a bit higher like M.warn_may and M.warn_must or may_unknown, must_integer and may_integer ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. You can also just define aliases instead of pulling them out. Or as I wrote above, just put all types in one module. If there's only going to be the type and show for it, modules for each just makes it unnecessarily verbose (I assume they're not going to be put in some functor).
I think in most modules Messages was imported as just M.
I meant the repeated M.x M.y M.z instead of M.(x y z).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another way to make it less verbose would be to have the certainty as an optional argument for warn and warn_each as it is already done with ?ctx:
M.(warn Integer "...")
M.(warn ~must:true Integer "...")
I assume we produce more may than must.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The optional must parameter is now there. I also added some nesting and helper methods to build the types from the bottom so now it should be possible to do:
M.warn (M.Warning.Integer.overflow ());
M.warn (M.Warning.Behavior.Undefined.nullpointer_dereference ());
M.warn ~must:true (M.Warning.Behavior.Undefined.ArrayOutOfBounds.before_start ());
also the variants are now not hidden inside modules so the top level variants can be used directly as:
M.warn (M.Unknown "my message");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vandah Previously, there was the Certainty.t option, which would allow for None in case of "Debug" warnings. How is this handled now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This remained there. The debug function still works the same.
Before, I just called warn from the debug function. Now there's a function warn_internal which does what warn did previously and is called from both warn and debug. This warn_internal takes the type WarningWithCertainty.t which still has the Certainty.t option inside.
LogEvent -> Warning EventType -> Warn IntegerEvent -> Integer> ...
also renames Warning -> WarningWithCertainty and WarnType -> Warning
also remove the array type because arrayoob is under UB
src/util/messages.ml
Outdated
| let to_string e = | ||
| match e with | ||
| | May -> "may" | ||
| | Must -> "must" | ||
|
|
||
| let should_warn e = | ||
| get_bool ("warn." ^ (to_string e)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same applies here.
also uses concatenation instead of redundant sprintf
and replace unused parameters with `_`
also adds optional location to warn_each
this eliminates the need to specify the regexes separately in update_suite.rb later it would probably be nice if the categories were parsed and checked there also changes formatting of the warning tags
also adds optional msg argument and removes string params from unknown and debug
|
I added to our GobCon agenda that we should discuss the follow-up work to this pull request next meeting. There is still a lot of open questions about the warning system, so it would probably be best to merge this pull request, and then proceed with somewhat smaller pull requests and discussions on all the remaining questions. |
This pull request closes #201
We introduced a must or may tag and the following categories:
We introduced the warning as mywarn, which can be renamed to the normal warn once all warnings have been replaced.The user can enable/disable the following flags(enabled by default) to filter warnings:
The structure inside messages.ml is the following:
module LogEvent with two fields:
• certainty: May | Must (optional, if it's None, then it's always printed -> this is used for debugging)
• event_type: Behavior | Integer | ... (these can bear additional information but it needs to be determined what information is useful for which warning type)
also has methods .may and .must to shorten the creation of may/must warnings (then it only takes the event_type as an argument)
Calling the warnings should be rather intuitive but here are some examples:
M.warn @@ M.Warning.Unknown "Extra info can be passed here";M.warn_each ~must:true @@ warn_type M.ArrayOOB.PastEnd;Path sensitivity was not taken into account as it was ruled out of scope.
But there are still some things open to discussion in our opinion:
1. The flags would also make sense in Std in our opinionWe will introduce a new category as suggested by @sim6422. What other categories should be introduced ?
3. Should the user be able to enable/disable subcategories ? Right now he can enable/disable the behavior category but not the subcategory of NullPointerDereference for example