labath added inline comments.

================
Comment at: lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp:246-248
+  DataExtractor data(&promise_addr, sizeof(promise_addr),
+                     process_sp->GetByteOrder(),
+                     process_sp->GetAddressByteSize());
----------------
avogelsgesang wrote:
> labath wrote:
> > avogelsgesang wrote:
> > > labath wrote:
> > > > Have you checked there won't be a use-after-free problem here, given 
> > > > that this data extractor will refer to the stack object?
> > > > 
> > > > To create persistent data, you need to use the DataBufferSP 
> > > > constructor, but I'm wondering if we couldn't fix this by creating the 
> > > > (non-pointer) object using the `CreateValueObjectFromAddress` function, 
> > > > as above, but then actually use valobj->AddressOf as the synthetic 
> > > > child.
> > > > 
> > > > I am also somewhat surprised that we need to use the GetAddressOf trick 
> > > > here, as this seems to indicate that the coroutine contains (in the 
> > > > proper C "subobject" kind of way) the promise object. That's not 
> > > > necessarily wrong, but it makes me think we may be "breaking the cycle" 
> > > > at the wrong place.
> > > Thanks for taking a look!
> > > 
> > > > To create persistent data, you need to use the DataBufferSP constructor
> > > 
> > > good point, I will keep this in mind as a fallback in case we don't 
> > > decide to follow any of the other directions you hinted at.
> > > 
> > > > wondering if we couldn't fix this by creating the (non-pointer) object 
> > > > using the CreateValueObjectFromAddress function, as above, but then 
> > > > actually use valobj->AddressOf as the synthetic child
> > > 
> > > Good idea! I will give it a try
> > > 
> > > 
> > > > [...] as this seems to indicate that the coroutine contains (in the 
> > > > proper C "subobject" kind of way) the promise object. That's not 
> > > > necessarily wrong, but it makes me think we may be "breaking the cycle" 
> > > > at the wrong place.
> > > 
> > > The physical layout of this is:
> > > ```
> > > // in the standard library
> > > template<typename promise_type>
> > > struct exception_handle<promise_type> {
> > >     __coro_frame<promise_type>* __hdl; // <--- this is the pointer we 
> > > read with `GetCoroFramePtrFromHandle`
> > > };
> > > 
> > > // compiler-generated coroutine frame. Generated ad-hoc per coroutine
> > > struct __coro_frame<promise_type> {
> > >      // The ABI guaranteees that hose two pointers are always the first 
> > > two pointers in the struct.
> > >      void (*resume)(void*); // function pointer for type erasure
> > >      void (*destroy)(void*); // function pointer for type erasure
> > >      // Next comes our promise type. This is under the control of the 
> > > program author
> > >      promise_type promise;
> > >      // Next comes any compiler-generated, internal state which gets 
> > > persisted across suspension points. 
> > >      // The functions pointed to by `resume`/`destroy` know how to 
> > > interpret this part of the coroutine frame.
> > >      int __suspension_point_id;
> > >      double __some_internal_state;
> > >      std::string __some_other_internal_state;
> > >      ....
> > > };
> > > ```
> > > 
> > > The programmer (i.e., most likely the user of this pretty-printer), wrote 
> > > only the "promise" explicitly in his code. Everything else is 
> > > compiler-generated. As such, the lldb-user will usually look for the 
> > > "promise" first, and I would like to make it easy to find it, by exposing 
> > > it as a top-level children of the `exception_handle` instead of hiding it 
> > > inside a sub-child.
> > > As such, the lldb-user will usually look for the "promise" first, and I 
> > > would like to make it easy to find it, by exposing it as a top-level 
> > > children of the `exception_handle` instead of hiding it inside a 
> > > sub-child.
> > 
> > That makes sense. And I think that's a good argument for automatically 
> > "dereferencing" that object (i.e., status quo). That said, it's not fully 
> > clear to me how do we end up looping here. I take it the promise object 
> > contains a (compiler-generated ?) pointer to another __coro_frame object? 
> > What would happen if we turned *that* into a pointer?
> > I take it the promise object contains a [...] pointer to another 
> > __coro_frame object?
> 
> yes
> 
> > (compiler-generated ?)
> 
> no
> 
> We end up looping if the user explicitly puts an `coroutine_handle` inside 
> `promise_type`. You can find an example of this in 
> https://clang.llvm.org/docs/DebuggingCoroutines.html#get-the-asynchronous-stack,
>  in particular
> 
> ```
> struct promise_type {
>     // [...] shortened for readability
> 
>     std::coroutine_handle<> continuation = std::noop_coroutine();
>     int result = 0;
> };
> ```
> 
> In asynchronous programming, it is common to "chain continuations" by putting 
> a `coroutine_handle` into the `promise_type`. This coroutine_handle inside 
> the promise_type gives my asynchronous coroutine the answer to the question 
> "where should I continue next, after finishing the asychronous operation 
> modelled by my own coroutine?".
> 
> In normal situations (i.e. in the absence of bugs inside the debugged 
> program), those coroutine_handle's should not form cycles. But I guess there 
> could also be other use cases for coroutines where coroutine_handle cycles 
> might be useful...
> 
> > What would happen if we turned *that* into a pointer?
> 
> The `promise_type` is a user-written struct, so I guess that I have little 
> leverage whether it contains a `coroutine_handle` by value or by pointer? And 
> turning an object which the user wrote as "by value" into a "by pointer" 
> representation in the debugger would be pretty surprising...
Ok, thanks for your patience, I think I finally understand what's going on. 
Given all of the above, the approach in this patch (not dereferencing the 
promise type) makes sense to me, since the `coroutine_handle`type has 
pointer-like semantics. However, do take my opinion with a grain of salt, since 
I probably not going to be using this code.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132815/new/

https://reviews.llvm.org/D132815

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to