[PATCH] D132451: [docs] Add examples for printing asynchronous stack for coroutines

2022-08-23 Thread Chuanqi Xu via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG4332b049edf6: [docs] Add examples for printing asynchronous 
stack for coroutines (authored by ChuanqiXu).

Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D132451

Files:
  clang/docs/DebuggingCoroutines.rst

Index: clang/docs/DebuggingCoroutines.rst
===
--- clang/docs/DebuggingCoroutines.rst
+++ clang/docs/DebuggingCoroutines.rst
@@ -368,6 +368,345 @@
 
 This logic should be quite easily captured in a debugger script.
 
+Examples to print asynchronous stack
+
+
+Here is an example to print the asynchronous stack for the normal task implementation.
+
+.. code-block:: c++
+
+  // debugging-example.cpp
+  #include 
+  #include 
+  #include 
+
+  struct task {
+struct promise_type {
+  task get_return_object();
+  std::suspend_always initial_suspend() { return {}; }
+  
+  void unhandled_exception() noexcept {}
+
+  struct FinalSuspend {
+std::coroutine_handle<> continuation;
+auto await_ready() noexcept { return false; }
+auto await_suspend(std::coroutine_handle<> handle) noexcept {
+  return continuation;
+}
+void await_resume() noexcept {}
+  };
+  FinalSuspend final_suspend() noexcept { return {continuation}; }
+
+  void return_value(int res) { result = res; }
+
+  std::coroutine_handle<> continuation = std::noop_coroutine();
+  int result = 0;
+};
+
+task(std::coroutine_handle handle) : handle(handle) {}
+~task() {
+  if (handle)
+handle.destroy();
+}
+
+auto operator co_await() {
+  struct Awaiter {
+std::coroutine_handle handle;
+auto await_ready() { return false; }
+auto await_suspend(std::coroutine_handle<> continuation) {
+  handle.promise().continuation = continuation;
+  return handle;
+}
+int await_resume() {
+  int ret = handle.promise().result;
+  handle.destroy();
+  return ret;
+}
+  };
+  return Awaiter{std::exchange(handle, nullptr)};
+}
+
+int syncStart() {
+  handle.resume();
+  return handle.promise().result;
+}
+
+  private:
+std::coroutine_handle handle;
+  };
+
+  task task::promise_type::get_return_object() {
+return std::coroutine_handle::from_promise(*this);
+  }
+
+  namespace detail {
+  template 
+  task chain_fn() {
+co_return N + co_await chain_fn();
+  }
+
+  template <>
+  task chain_fn<0>() {
+// This is the default breakpoint.
+__builtin_debugtrap();
+co_return 0;
+  }
+  }  // namespace detail
+
+  task chain() {
+co_return co_await detail::chain_fn<30>();
+  }
+
+  int main() {
+std::cout << chain().syncStart() << "\n";
+return 0;
+  }
+
+In the example, the ``task`` coroutine holds a ``continuation`` field,
+which would be resumed once the ``task`` completes.
+In another word, the ``continuation`` is the asynchronous caller for the ``task``.
+Just like the normal function returns to its caller when the function completes.
+
+So we can use the ``continuation`` field to construct the asynchronous stack:
+
+.. code-block:: python
+
+  # debugging-helper.py
+  import gdb
+  from gdb.FrameDecorator import FrameDecorator
+
+  class SymValueWrapper():
+  def __init__(self, symbol, value):
+  self.sym = symbol
+  self.val = value
+
+  def __str__(self):
+  return str(self.sym) + " = " + str(self.val)
+
+  def get_long_pointer_size():
+  return gdb.lookup_type('long').pointer().sizeof
+
+  def cast_addr2long_pointer(addr):
+  return gdb.Value(addr).cast(gdb.lookup_type('long').pointer())
+
+  def dereference(addr):
+  return long(cast_addr2long_pointer(addr).dereference())
+
+  class CoroutineFrame(object):
+  def __init__(self, task_addr):
+  self.frame_addr = task_addr
+  self.resume_addr = task_addr
+  self.destroy_addr = task_addr + get_long_pointer_size()
+  self.promise_addr = task_addr + get_long_pointer_size() * 2
+  # In the example, the continuation is the first field member of the promise_type.
+  # So they have the same addresses.
+  # If we want to generalize the scripts to other coroutine types, we need to be sure
+  # the continuation field is the first memeber of promise_type.
+  self.continuation_addr = self.promise_addr
+
+  def next_task_addr(self):
+  return dereference(self.continuation_addr)
+
+  class CoroutineFrameDecorator(FrameDecorator):
+  def __init__(self, coro_frame):
+  super(CoroutineFrameDecorator, self).__init__(None)
+  self.coro_frame = coro_frame
+  self.resume_func = 

[PATCH] D132451: [docs] Add examples for printing asynchronous stack for coroutines

2022-08-23 Thread Chuanqi Xu via Phabricator via cfe-commits
ChuanqiXu added a comment.

In D132451#3742064 , @avogelsgesang 
wrote:

> LGTM, thanks!
>
> I guess I will update this documentation with LLDB examples, as soon as 
> https://reviews.llvm.org/D132415 and a couple of follow-up improvements 
> landed.
> As soon as that happened, we will also have LLDB covered here. I see no 
> problem with covering mostly gdb for the time being, given that lldb 
> debugging is currently subject to change, anyway

Got it. I feel better to add LLDB support too : )


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

https://reviews.llvm.org/D132451

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


[PATCH] D132451: [docs] Add examples for printing asynchronous stack for coroutines

2022-08-23 Thread Adrian Vogelsgesang via Phabricator via cfe-commits
avogelsgesang accepted this revision.
avogelsgesang added a comment.
This revision is now accepted and ready to land.

LGTM, thanks!

I guess I will update this documentation with LLDB examples, as soon as 
https://reviews.llvm.org/D132415 and a couple of follow-up improvements landed.
As soon as that happened, we will also have LLDB covered here. I see no problem 
with covering mostly gdb for the time being, given that lldb debugging is 
currently subject to change, anyway


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

https://reviews.llvm.org/D132451

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


[PATCH] D132451: [docs] Add examples for printing asynchronous stack for coroutines

2022-08-23 Thread Chuanqi Xu via Phabricator via cfe-commits
ChuanqiXu added inline comments.



Comment at: clang/docs/DebuggingCoroutines.rst:619
+
+.. code-block:: text
+

Here we can't use `.. code-block:: console` since it will meet some parsing 
problems.


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

https://reviews.llvm.org/D132451

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


[PATCH] D132451: [docs] Add examples for printing asynchronous stack for coroutines

2022-08-23 Thread Chuanqi Xu via Phabricator via cfe-commits
ChuanqiXu updated this revision to Diff 454745.
ChuanqiXu added a comment.

Fix a typo.


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

https://reviews.llvm.org/D132451

Files:
  clang/docs/DebuggingCoroutines.rst

Index: clang/docs/DebuggingCoroutines.rst
===
--- clang/docs/DebuggingCoroutines.rst
+++ clang/docs/DebuggingCoroutines.rst
@@ -368,6 +368,345 @@
 
 This logic should be quite easily captured in a debugger script.
 
+Examples to print asynchronous stack
+
+
+Here is an example to print the asynchronous stack for the normal task implementation.
+
+.. code-block:: c++
+
+  // debugging-example.cpp
+  #include 
+  #include 
+  #include 
+
+  struct task {
+struct promise_type {
+  task get_return_object();
+  std::suspend_always initial_suspend() { return {}; }
+  
+  void unhandled_exception() noexcept {}
+
+  struct FinalSuspend {
+std::coroutine_handle<> continuation;
+auto await_ready() noexcept { return false; }
+auto await_suspend(std::coroutine_handle<> handle) noexcept {
+  return continuation;
+}
+void await_resume() noexcept {}
+  };
+  FinalSuspend final_suspend() noexcept { return {continuation}; }
+
+  void return_value(int res) { result = res; }
+
+  std::coroutine_handle<> continuation = std::noop_coroutine();
+  int result = 0;
+};
+
+task(std::coroutine_handle handle) : handle(handle) {}
+~task() {
+  if (handle)
+handle.destroy();
+}
+
+auto operator co_await() {
+  struct Awaiter {
+std::coroutine_handle handle;
+auto await_ready() { return false; }
+auto await_suspend(std::coroutine_handle<> continuation) {
+  handle.promise().continuation = continuation;
+  return handle;
+}
+int await_resume() {
+  int ret = handle.promise().result;
+  handle.destroy();
+  return ret;
+}
+  };
+  return Awaiter{std::exchange(handle, nullptr)};
+}
+
+int syncStart() {
+  handle.resume();
+  return handle.promise().result;
+}
+
+  private:
+std::coroutine_handle handle;
+  };
+
+  task task::promise_type::get_return_object() {
+return std::coroutine_handle::from_promise(*this);
+  }
+
+  namespace detail {
+  template 
+  task chain_fn() {
+co_return N + co_await chain_fn();
+  }
+
+  template <>
+  task chain_fn<0>() {
+// This is the default breakpoint.
+__builtin_debugtrap();
+co_return 0;
+  }
+  }  // namespace detail
+
+  task chain() {
+co_return co_await detail::chain_fn<30>();
+  }
+
+  int main() {
+std::cout << chain().syncStart() << "\n";
+return 0;
+  }
+
+In the example, the ``task`` coroutine holds a ``continuation`` field,
+which would be resumed once the ``task`` completes.
+In another word, the ``continuation`` is the asynchronous caller for the ``task``.
+Just like the normal function returns to its caller when the function completes.
+
+So we can use the ``continuation`` field to construct the asynchronous stack:
+
+.. code-block:: python
+
+  # debugging-helper.py
+  import gdb
+  from gdb.FrameDecorator import FrameDecorator
+
+  class SymValueWrapper():
+  def __init__(self, symbol, value):
+  self.sym = symbol
+  self.val = value
+
+  def __str__(self):
+  return str(self.sym) + " = " + str(self.val)
+
+  def get_long_pointer_size():
+  return gdb.lookup_type('long').pointer().sizeof
+
+  def cast_addr2long_pointer(addr):
+  return gdb.Value(addr).cast(gdb.lookup_type('long').pointer())
+
+  def dereference(addr):
+  return long(cast_addr2long_pointer(addr).dereference())
+
+  class CoroutineFrame(object):
+  def __init__(self, task_addr):
+  self.frame_addr = task_addr
+  self.resume_addr = task_addr
+  self.destroy_addr = task_addr + get_long_pointer_size()
+  self.promise_addr = task_addr + get_long_pointer_size() * 2
+  # In the example, the continuation is the first field member of the promise_type.
+  # So they have the same addresses.
+  # If we want to generalize the scripts to other coroutine types, we need to be sure
+  # the continuation field is the first memeber of promise_type.
+  self.continuation_addr = self.promise_addr
+
+  def next_task_addr(self):
+  return dereference(self.continuation_addr)
+
+  class CoroutineFrameDecorator(FrameDecorator):
+  def __init__(self, coro_frame):
+  super(CoroutineFrameDecorator, self).__init__(None)
+  self.coro_frame = coro_frame
+  self.resume_func = dereference(self.coro_frame.resume_addr)
+  self.resume_func_block = gdb.block_for_pc(self.resume_func)
+  if self.resume_func_block == None:
+  raise Exception('Not stackless coroutine.')
+   

[PATCH] D132451: [docs] Add examples for printing asynchronous stack for coroutines

2022-08-23 Thread Chuanqi Xu via Phabricator via cfe-commits
ChuanqiXu updated this revision to Diff 454744.
ChuanqiXu edited the summary of this revision.
ChuanqiXu added a comment.

Fix a typo.


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

https://reviews.llvm.org/D132451

Files:
  clang/docs/DebuggingCoroutines.rst

Index: clang/docs/DebuggingCoroutines.rst
===
--- clang/docs/DebuggingCoroutines.rst
+++ clang/docs/DebuggingCoroutines.rst
@@ -368,6 +368,345 @@
 
 This logic should be quite easily captured in a debugger script.
 
+Examples to print asynchronous stack
+
+
+Here is an example to print the asynchronous stack for the normal task implementation.
+
+.. code-block:: c++
+
+  // debugging-example.cpp
+  #include 
+  #include 
+  #include 
+
+  struct task {
+struct promise_type {
+  task get_return_object();
+  std::suspend_always initial_suspend() { return {}; }
+  
+  void unhandled_exception() noexcept {}
+
+  struct FinalSuspend {
+std::coroutine_handle<> continuation;
+auto await_ready() noexcept { return false; }
+auto await_suspend(std::coroutine_handle<> handle) noexcept {
+  return continuation;
+}
+void await_resume() noexcept {}
+  };
+  FinalSuspend final_suspend() noexcept { return {continuation}; }
+
+  void return_value(int res) { result = res; }
+
+  std::coroutine_handle<> continuation = std::noop_coroutine();
+  int result = 0;
+};
+
+task(std::coroutine_handle handle) : handle(handle) {}
+~task() {
+  if (handle)
+handle.destroy();
+}
+
+auto operator co_await() {
+  struct Awaiter {
+std::coroutine_handle handle;
+auto await_ready() { return false; }
+auto await_suspend(std::coroutine_handle<> continuation) {
+  handle.promise().continuation = continuation;
+  return handle;
+}
+int await_resume() {
+  int ret = handle.promise().result;
+  handle.destroy();
+  return ret;
+}
+  };
+  return Awaiter{std::exchange(handle, nullptr)};
+}
+
+int syncStart() {
+  handle.resume();
+  return handle.promise().result;
+}
+
+  private:
+std::coroutine_handle handle;
+  };
+
+  task task::promise_type::get_return_object() {
+return std::coroutine_handle::from_promise(*this);
+  }
+
+  namespace detail {
+  template 
+  task chain_fn() {
+co_return N + co_await chain_fn();
+  }
+
+  template <>
+  task chain_fn<0>() {
+// This is the default breakpoint.
+__builtin_debugtrap();
+co_return 0;
+  }
+  }  // namespace detail
+
+  task chain() {
+co_return co_await detail::chain_fn<30>();
+  }
+
+  int main() {
+std::cout << chain().syncStart() << "\n";
+return 0;
+  }
+
+In the example, the ``task`` coroutine holds a ``continuation`` field,
+which would be resumed once the ``task`` completes.
+In another word, the ``continuation`` is the asynchronous caller for the ``task``.
+Just like the normal function returns to its caller when the function completes.
+
+So we can use the ``continuation`` field to construct the asynchronous stack:
+
+.. code-block:: python
+
+  # debugging-helper.py
+  import gdb
+  from gdb.FrameDecorator import FrameDecorator
+
+  class SymValueWrapper():
+  def __init__(self, symbol, value):
+  self.sym = symbol
+  self.val = value
+
+  def __str__(self):
+  return str(self.sym) + " = " + str(self.val)
+
+  def get_long_pointer_size():
+  return gdb.lookup_type('long').pointer().sizeof
+
+  def cast_addr2long_pointer(addr):
+  return gdb.Value(addr).cast(gdb.lookup_type('long').pointer())
+
+  def dereference(addr):
+  return long(cast_addr2long_pointer(addr).dereference())
+
+  class CoroutineFrame(object):
+  def __init__(self, task_addr):
+  self.frame_addr = task_addr
+  self.resume_addr = task_addr
+  self.destroy_addr = task_addr + get_long_pointer_size()
+  self.promise_addr = task_addr + get_long_pointer_size() * 2
+  # In the example, the continuation is the first field member of the promise_type.
+  # So they have the same addresses.
+  # If we want to generalize the scripts to other coroutine types, we need to be sure
+  # the continuation field is the first memeber of promise_type.
+  self.continuation_addr = self.promise_addr
+
+  def next_task_addr(self):
+  return dereference(self.continuation_addr)
+
+  class CoroutineFrameDecorator(FrameDecorator):
+  def __init__(self, coro_frame):
+  super(CoroutineFrameDecorator, self).__init__(None)
+  self.coro_frame = coro_frame
+  self.resume_func = dereference(self.coro_frame.resume_addr)
+  self.resume_func_block = gdb.block_for_pc(self.resume_func)
+  if self.resume_func_block == None:
+  

[PATCH] D132451: [docs] Add examples for printing asynchronous stack for coroutines

2022-08-23 Thread Chuanqi Xu via Phabricator via cfe-commits
ChuanqiXu created this revision.
ChuanqiXu added reviewers: avogelsgesang, dblaikie, aprantl, labath, 
JDevlieghere.
ChuanqiXu added a project: debug-info.
Herald added a project: All.
ChuanqiXu requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Previously when I wrote this document, I felt the completed scripts was 
lengthy, redundant and not easy to read. So I didn't add complete examples in 
the previous commit.

However, in the recent discussion with @avogelsgesang, I found people may not 
know how to use debugging scripts to improve their debugging efficiency. So 
now, I feel like it is helpful to put the examples even if they are a little 
bit long.

I understand it may be better to provide debugging scripts for lldb too since 
we are in LLVM community : ) But I never used lldb before... So I put the 
examples for gdb only. I guess this won't be a blocking issue?


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D132451

Files:
  clang/docs/DebuggingCoroutines.rst

Index: clang/docs/DebuggingCoroutines.rst
===
--- clang/docs/DebuggingCoroutines.rst
+++ clang/docs/DebuggingCoroutines.rst
@@ -368,6 +368,345 @@
 
 This logic should be quite easily captured in a debugger script.
 
+Examples to printasynchronous stack
+---
+
+Here is an example to print the asynchronous stack for the normal task implementation.
+
+.. code-block:: c++
+
+  // debugging-example.cpp
+  #include 
+  #include 
+  #include 
+
+  struct task {
+struct promise_type {
+  task get_return_object();
+  std::suspend_always initial_suspend() { return {}; }
+  
+  void unhandled_exception() noexcept {}
+
+  struct FinalSuspend {
+std::coroutine_handle<> continuation;
+auto await_ready() noexcept { return false; }
+auto await_suspend(std::coroutine_handle<> handle) noexcept {
+  return continuation;
+}
+void await_resume() noexcept {}
+  };
+  FinalSuspend final_suspend() noexcept { return {continuation}; }
+
+  void return_value(int res) { result = res; }
+
+  std::coroutine_handle<> continuation = std::noop_coroutine();
+  int result = 0;
+};
+
+task(std::coroutine_handle handle) : handle(handle) {}
+~task() {
+  if (handle)
+handle.destroy();
+}
+
+auto operator co_await() {
+  struct Awaiter {
+std::coroutine_handle handle;
+auto await_ready() { return false; }
+auto await_suspend(std::coroutine_handle<> continuation) {
+  handle.promise().continuation = continuation;
+  return handle;
+}
+int await_resume() {
+  int ret = handle.promise().result;
+  handle.destroy();
+  return ret;
+}
+  };
+  return Awaiter{std::exchange(handle, nullptr)};
+}
+
+int syncStart() {
+  handle.resume();
+  return handle.promise().result;
+}
+
+  private:
+std::coroutine_handle handle;
+  };
+
+  task task::promise_type::get_return_object() {
+return std::coroutine_handle::from_promise(*this);
+  }
+
+  namespace detail {
+  template 
+  task chain_fn() {
+co_return N + co_await chain_fn();
+  }
+
+  template <>
+  task chain_fn<0>() {
+// This is the default breakpoint.
+__builtin_debugtrap();
+co_return 0;
+  }
+  }  // namespace detail
+
+  task chain() {
+co_return co_await detail::chain_fn<30>();
+  }
+
+  int main() {
+std::cout << chain().syncStart() << "\n";
+return 0;
+  }
+
+In the example, the ``task`` coroutine holds a ``continuation`` field,
+which would be resumed once the ``task`` completes.
+In another word, the ``continuation`` is the asynchronous caller for the ``task``.
+Just like the normal function returns to its caller when the function completes.
+
+So we can use the ``continuation`` field to construct the asynchronous stack:
+
+.. code-block:: python
+
+  # debugging-helper.py
+  import gdb
+  from gdb.FrameDecorator import FrameDecorator
+
+  class SymValueWrapper():
+  def __init__(self, symbol, value):
+  self.sym = symbol
+  self.val = value
+
+  def __str__(self):
+  return str(self.sym) + " = " + str(self.val)
+
+  def get_long_pointer_size():
+  return gdb.lookup_type('long').pointer().sizeof
+
+  def cast_addr2long_pointer(addr):
+  return gdb.Value(addr).cast(gdb.lookup_type('long').pointer())
+
+  def dereference(addr):
+  return long(cast_addr2long_pointer(addr).dereference())
+
+  class CoroutineFrame(object):
+  def __init__(self, task_addr):
+  self.frame_addr = task_addr
+  self.resume_addr = task_addr
+  self.destroy_addr = task_addr + get_long_pointer_size()
+  self.promise_addr = task_addr + get_long_pointer_size() * 2
+  # In the example, the continuation is the first field member of