-
Notifications
You must be signed in to change notification settings - Fork 108
Provide ability to disallow watchpoints inside certain containers #1545
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
base: devel
Are you sure you want to change the base?
Conversation
|
CLANG-FORMAT TEST - FAILED (on last commit): |
65ca488 to
5924aa3
Compare
|
CLANG-FORMAT TEST - FAILED (on last commit): |
…ainer types which may invalidate references
5924aa3 to
17e8bdc
Compare
|
CLANG-FORMAT TEST - PASSED |
|
Status Flag 'Pre-Test Inspection' - - This Pull Request Requires Inspection... The code must be inspected by a member of the Team before Testing/Merging |
|
CLANG-FORMAT TEST - PASSED |
|
Probably need to add std::vector, std::unordered_map and std::unordered_set to the unwatchable list. All of these can reallocate on inserts and/or deletes. There may be others, but I'll need to look to see if the data addresses are stable for things like deques. |
|
The adapter classes All containers in I would attempt to fix the issue which #1517 started addressing, rather than adding blanket rules against watchpoints in certain containers. Please reference this page for iterator/reference invalidation rules. |
|
A test failure having to do with As was made clear in #1517, it is a more general issue of C++ container modification invalidating iterators/references to their elements. It has nothing to do with So I do not agree with this PR, which singles out certain classes which have failed tests in the past, rather than addressing the real underlying issue. |
|
Thanks for the feedback. I should clarify that this isn't intended, by any stretch of the imagination, to be a long term solution for watchpoints. Instead, it is a temporary patch to help us avoid ( and focus on ) a few known problematic cases. |
|
I think that only |
|
Converting this back to draft |
|
I would suggest that type traits be defined and used to query if a type should be excluded, because type traits can be evaluated at compile-time (better optimization as well as the ability to give compile-time errors if desired if a condition is true), and because their trait "specialization" definitions can be placed in the In Refer to https://en.cppreference.com/w/cpp/header/type_traits.html for the standard type traits, and define yours like I defined them in Depending on the circumstances, it may make more sense to define the trait class (with the Traits can be specialized for subsets of their arguments. For adapter classes // The shortened name boolean trait template -- this should be declared somewhere which is compiled first, like serialize.h
template<typename T>
constexpr bool is_watchpoint_disallowed_v = false;
// The trait name without _v at the end, defined in terms of the shortened _v trait name
template<typename T>
using is_watchpoint_disallowed = std::bool_constant<is_watchpoint_disallowed_v<T>>;
// Specialization for std::queue<T, C> defined as the OR of whether T or C is disallowed
template<typename T, typename C>
constexpr bool is_watchpoint_disallowed_v<std::queue<T, C>> = std::disjunction_v<is_watchpoint_disallowed<T>, is_watchpoint_disallowed<C>>;Here there is not a blanked rule against disallowing watchpoints on all
template<typename T, typename = void>
constexpr bool is_watchpoint_disallowed_v = false;
template<typename T, typename C, template<typename, typename> class A>
constexpr bool is_watchpoint_disallowed_v<A<T,C>, std::enable_if_t<is_adapter_v<A<T,C>>>> = std::disjunction_v<is_watchpoint_disallowed<T>, is_watchpoint_disallowed<C>>;The last line (or something like it) would be the only thing which needs to be added to My preferred solution would be to not ban watchpoints outright, but to try to find a solution to the problem of refreshing the If you're concerned about performance, then since references to some elements of all containers' can be invalidated by at least one operation on the container (such as deletion of the element), then a warning about possibly long debugging time should be printed if a watchpoint is made on a container or its elements: template<typename T, template<typename...> class... CONTAINERS>
constexpr bool matches_some_template_v = std::disjunction_v<is_same_type_template<T, CONTAINERS>...>;
template<typename T>
constexpr bool is_container_v = matches_some_template_v<T, std::vector, std::map, std::multimap, std::deque, std::unordered_map, std::unordered_multimap, std::set, std::unordered_set, std::multiset, std::unordered_multiset, std::list, std::forward_list, std::stack, std::queue, std::priority_queue>;This simple definition pair compares a type It is a simple compile-time test which can tell whether a type is an instance of one those containers/adapters. If you want to make it a compile-time, error, you can use |
|
Also note that all of this may be redundant. SST already has an Note that So |
|
Thanks Lee! Really appreciate the time and energy you've put into this- |
|
I agree with Lee that any container with variable size could have an issue with invalidating elements, but the issue of "watchable" goes so much deeper than containers. Really, any pointer, whether stored in a container or not, can be "invalidated" by being deleted somewhere along the way, and any WatchPoint that tries to access it after being deleted risks a segfault. I think we'll need to rethink the ObjectMaps for pointer types. We may need to keep the address to where the pointer is stored, add a note that anything that gets deleted should have it's pointer to set to nullptr, and then check that the value of the pointer is nullptr before dereferencing it. This, of course, won't catch all cases, but it could catch a fair number of them and avoid segfaults in those cases. We need to have a deep discussion about watchability and the potential pitfalls. I'm sure there are still cases we haven't thought about yet. |
#1517 was required since some container types like
std::queueinvalidate references causing the debugger to crash or simply report stale values. A 'refresh' function was also provided as a way to improve performance by only refreshing the object from the currentpwddown.Besides the likely performance issues, refreshing all or part of the object map to support
watchpointson elements in these containers has the additional requirement of updated the references stored in the watchpoint objects. Until this is resolved, we should disallow elements in these containers from being watched.To achieve this I added an 'isWatchable' function to the ObjectMap. The function normally returns true but returns false for types defined in
serialize_adapter.h. Namely,std::stack, std::queue, std::priority_queue. Users can be advised to simple provide data members that capture data accesses and/or control for these types.When traveling down the object map, if a non-watchable type is detected, that object is saved. The user then cannot set a watchpoint on any element below this level of hierarchy. When traversing up the hierarchy, this object pointer is cleared and watchpoints can then be set.
With this PR in place, we can start looking at options for handling watchpoints efficiently for these cases. Additionally, if testing uncovers other cases, we have a method to exclude them from watchpoint support.