Skip to content

Implement runtime stacktraces #1344

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

Merged

Conversation

Thirumalai-Shaktivel
Copy link
Collaborator

No description provided.

@Thirumalai-Shaktivel Thirumalai-Shaktivel force-pushed the runtime_stacktrace branch 3 times, most recently from 43580a1 to d3df449 Compare December 2, 2022 10:04
@Thirumalai-Shaktivel
Copy link
Collaborator Author

Thirumalai-Shaktivel commented Dec 2, 2022

I added unwind_callback in the intrinsics.
Now, should I use this like as a decorator or function call in Python (to verify that we get the correct debug information.)
Can you please share what should I do next?
@certik

@certik
Copy link
Contributor

certik commented Dec 2, 2022

Yes, I would check manually that the addresses that this returns are correct. I would also expose this so that the user can call it. Let's put it into ltypes, something like "ltypes.get_stacktrace_addresses()".

@certik
Copy link
Contributor

certik commented Dec 3, 2022

Here is how to print the runtime stacktrace:

diff --git a/examples/expr2.py b/examples/expr2.py
index 2e66f1e58..f58eda174 100644
--- a/examples/expr2.py
+++ b/examples/expr2.py
@@ -1,6 +1,13 @@
+def g():
+    assert True
+
+def f():
+    g()
+
 def main0():
     x: i32
     x = (2+3)*5
+    f()
     print(x)
 
 main0()
diff --git a/src/libasr/codegen/asr_to_llvm.cpp b/src/libasr/codegen/asr_to_llvm.cpp
index e7144a025..de974de8d 100644
--- a/src/libasr/codegen/asr_to_llvm.cpp
+++ b/src/libasr/codegen/asr_to_llvm.cpp
@@ -4900,6 +4900,7 @@ public:
     }
 
     void visit_Assert(const ASR::Assert_t &x) {
+        call_print_stacktrace_addresses(context, *module, *builder, {});
         this->visit_expr_wrapper(x.m_test, true);
         create_if_else(tmp, []() {}, [=]() {
             if (x.m_msg) {

This prints:

$ lpython examples/expr2.py 
0 10008b8bb
1 10008b9d3
2 100067e53
3 100067e43
4 100067e6f
5 100067e33
6 100067e9f
25

if (info->dlpi_phdr[i].p_type == PT_LOAD) {
ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
ElfW(Addr) max_addr = min_addr + info->dlpi_phdr[i].p_memsz;
printf("%lx\n", d->current_pc);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prints 0, I think the value is not passed correctly.
@certik any idea why does this prints 0?

Comment on lines 1232 to 1234
if (dl_iterate_phdr(shared_lib_callback, &d) == 0) {
// printf("%d %lx %lx\n", i, d->pc[i], d->local_pc);
// printf("%lx\n", d->current_pc);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, it prints the correct value.

Comment on lines 1196 to 1204
struct dl_phdr_info {
ElfW(Addr) dlpi_addr;
const char *dlpi_name;
const ElfW(Phdr) *dlpi_phdr;
ElfW(Half) dlpi_phnum;
};

extern int dl_iterate_phdr (int (*__callback) (struct dl_phdr_info *,
size_t, void *), void *__data);
Copy link
Collaborator Author

@Thirumalai-Shaktivel Thirumalai-Shaktivel Dec 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the problem can be caused by this as well.
I added this because we get the following warning and error

home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1208:32: warning: ‘struct dl_phdr_infodeclared inside parameter list will not be visible outside of this definition or declaration
 1208 | int shared_lib_callback(struct dl_phdr_info *info,
      |                                ^~~~~~~~~~~~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c: In functionshared_lib_callback’:
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1211:29: error: invalid use of undefined type ‘struct dl_phdr_info1211 |     for (int i = 0; i < info->dlpi_phnum; i++) {
      |                             ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1212:17: error: invalid use of undefined typestruct dl_phdr_info1212 |         if (info->dlpi_phdr[i].p_type == PT_LOAD) {
      |                 ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1213:39: error: invalid use of undefined typestruct dl_phdr_info1213 |             ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
      |                                       ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1213:57: error: invalid use of undefined typestruct dl_phdr_info1213 |             ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
      |                                                         ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1214:50: error: invalid use of undefined typestruct dl_phdr_info1214 |             ElfW(Addr) max_addr = min_addr + info->dlpi_phdr[i].p_memsz;
      |                                                  ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1220:51: error: invalid use of undefined typestruct dl_phdr_info1220 |                 d->local_pc = d->current_pc - info->dlpi_addr;
      |                                                   ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1221:37: error: invalid use of undefined typestruct dl_phdr_info1221 |                 printf("%lx\n", info->dlpi_addr);
      |                                     ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1208:32: warning: ‘struct dl_phdr_infodeclared inside parameter list will not be visible outside of this definition or declaration
 1208 | int shared_lib_callback(struct dl_phdr_info *info,
      |                                ^~~~~~~~~~~~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c: In functionshared_lib_callback’:
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1211:29: error: invalid use of undefined type ‘struct dl_phdr_info1211 |     for (int i = 0; i < info->dlpi_phnum; i++) {
      |                             ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1212:17: error: invalid use of undefined typestruct dl_phdr_info1212 |         if (info->dlpi_phdr[i].p_type == PT_LOAD) {
      |                 ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1213:39: error: invalid use of undefined typestruct dl_phdr_info1213 |             ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
      |                                       ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1213:57: error: invalid use of undefined typestruct dl_phdr_info1213 |             ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
      |                                                         ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1214:50: error: invalid use of undefined typestruct dl_phdr_info1214 |             ElfW(Addr) max_addr = min_addr + info->dlpi_phdr[i].p_memsz;
      |                                                  ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1220:51: error: invalid use of undefined typestruct dl_phdr_info1220 |                 d->local_pc = d->current_pc - info->dlpi_addr;
      |                                                   ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1221:37: error: invalid use of undefined typestruct dl_phdr_info1221 |                 printf("%lx\n", info->dlpi_addr);
      |                                     ^~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c: In functionprint_stacktrace_addresses’:
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1235:13: warning: implicit declaration of functiondl_iterate_phdr’ [-Wimplicit-function-declaration]
 1235 |         if (dl_iterate_phdr(shared_lib_callback, &d) == 0) {
      |             ^~~~~~~~~~~~~~~
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c: In functionprint_stacktrace_addresses’:
/home/thirumalai/Open_Source/lpython/src/libasr/runtime/lfortran_intrinsics.c:1235:13: warning: implicit declaration of functiondl_iterate_phdr’ [-Wimplicit-function-declaration]
 1235 |         if (dl_iterate_phdr(shared_lib_callback, &d) == 0) {
      |             ^~~~~~~~~~~~~~~
make[2]: *** [src/runtime/legacy/CMakeFiles/lpython_runtime_static.dir/build.make:76: src/runtime/legacy/CMakeFiles/lpython_runtime_static.dir/__/__/libasr/runtime/lfortran_intrinsics.c.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:477: src/runtime/legacy/CMakeFiles/lpython_runtime_static.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
make[2]: *** [src/runtime/legacy/CMakeFiles/lpython_runtime.dir/build.make:76: src/runtime/legacy/CMakeFiles/lpython_runtime.dir/__/__/libasr/runtime/lfortran_intrinsics.c.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:451: src/runtime/legacy/CMakeFiles/lpython_runtime.dir/all] Error 2

@certik
Copy link
Contributor

certik commented Dec 4, 2022

Not sure, we just need to debug it.

@Thirumalai-Shaktivel
Copy link
Collaborator Author

I was able to debug and fix the issue:
Here are the current output of print_stacktrace:

/home/thirumalai/Open_Source/lpython/src/bin/../runtime/liblpython_runtime.so
0 7fba4418ed0b: pc
7d0b: local_pc
/home/thirumalai/Open_Source/lpython/src/bin/../runtime/liblpython_runtime.so
1 7fba4418ef23: pc
7f23: local_pc

2 55bf57cca167: pc
1167: local_pc

3 55bf57cca155: pc
1155: local_pc

4 55bf57cca175: pc
1175: local_pc

5 55bf57cca145: pc
1145: local_pc

6 55bf57cca185: pc
1185: local_pc
/lib/x86_64-linux-gnu/libc.so.6
7 7fba43e90d8f: pc
29d8f: local_pc
/lib/x86_64-linux-gnu/libc.so.6
8 7fba43e90e3f: pc
29e3f: local_pc

9 55bf57cca074: pc
1074: local_pc

and llvm_dwarfdump:

Address            Line   Column File   ISA Discriminator Flags
------------------ ------ ------ ------ --- ------------- -------------
0x0000000000001140      0      0      1   0             0  is_stmt
0x0000000000001141     10      1      1   0             0  is_stmt prologue_end
0x0000000000001150      0      0      1   0             0  is_stmt
0x0000000000001151      2      5      1   0             0  is_stmt prologue_end
0x0000000000001160      0      0      1   0             0  is_stmt
0x0000000000001161      5      5      1   0             0  is_stmt prologue_end
0x0000000000001170      0      0      1   0             0  is_stmt
0x0000000000001171      8      5      1   0             0  is_stmt prologue_end
0x0000000000001180      0      0      1   0             0  is_stmt
0x0000000000001181      1      1      1   0             0  is_stmt prologue_end
0x000000000000118a      1      1      1   0             0  is_stmt end_sequence

I think we are getting the almost near address.
file:

     1	def f():
     2	    g()
     3	
     4	def g():
     5	    assert True
     6	
     7	def main0():
     8	    f()
     9	
    10	main0()
    11	
    12	# Not implemented yet in LPython:
    13	#if __name__ == "__main__":
    14	#    main()

@Thirumalai-Shaktivel
Copy link
Collaborator Author

Now, I will try to map the address and report back.

@Thirumalai-Shaktivel Thirumalai-Shaktivel force-pushed the runtime_stacktrace branch 2 times, most recently from 4784db2 to 56ca046 Compare December 9, 2022 10:58
@Thirumalai-Shaktivel
Copy link
Collaborator Author

Thirumalai-Shaktivel commented Dec 9, 2022

@certik how does dwarf_convert.py work?
I ask this because the lines.dat doesn't seem to be correct:
lines.dat: size

A: 144
$ cat lines.txt
1
examples/expr2.py
6

Are the above contents correct? is this the expected output from dwarf_convert?

@Thirumalai-Shaktivel
Copy link
Collaborator Author

Thirumalai-Shaktivel commented Dec 9, 2022

with open(sys.argv[3], "wb") as f:
for addr, line, fileid in asr.addresses:
f.write(pack("3Q", addr, line, fileid))

From above code, the contents seem to be correct print (addr, line)

4417 10
4433 2
4449 5
4481 8
4497 1
4506 1

How do I unpack this correctly?

@Thirumalai-Shaktivel
Copy link
Collaborator Author

Why convert to lines.dat, we could have written to text file, right?

@Thirumalai-Shaktivel
Copy link
Collaborator Author

Here is the current ouput:

examples/expr2.py: 140371440169592
	#    main()

examples/expr2.py: 8
	    f()

examples/expr2.py: 5
	    assert True

examples/expr2.py: 1
	def f():

examples/expr2.py: 2
	    g()

examples/expr2.py: 140371440169592
	#    main()

There are some bugs, I will debug it tomorrow.

@certik
Copy link
Contributor

certik commented Dec 9, 2022

Excellent progress!

I'll have time to meet next week if you need my help. This is good.

@Thirumalai-Shaktivel
Copy link
Collaborator Author

Thirumalai-Shaktivel commented Dec 10, 2022

This PR is ready for review!
Here is the stack trace output:

Screenshot from 2022-12-10 12-03-50

There are two things to do next:

  • Last two line from the above image is not required or doesn't show the correct information.

  • There is some file information that we need to handle. Here is the output of the binary_filename array:

     0: /home/thirumalai/Open_Source/lpython/src/bin/../runtime/liblpython_runtime.so
     1: /home/thirumalai/Open_Source/lpython/src/bin/../runtime/liblpython_runtime.so
     2: examples/expr2.py
     3: examples/expr2.py
     4: examples/expr2.py
     5: examples/expr2.py
     6: examples/expr2.py
     7: /lib/x86_64-linux-gnu/libc.so.6
     8: /lib/x86_64-linux-gnu/libc.so.6
     9: examples/expr2.py
    

@Thirumalai-Shaktivel
Copy link
Collaborator Author

Thirumalai-Shaktivel commented Dec 10, 2022

Reproduce the output using:

$ lpython examples/expr2.py -g --debug-with-line-column -o a.out
$ llvm-dwarfdump --debug-line a.out > a.txt
$ (cd src/bin; ./dwarf_convert.py ../../a.txt ../../lines.txt ../../lines.dat)
$ ./src/bin/dat_convert.py lines.dat
$ ./a.out
  File "examples/expr2.py", line 5
    assert True
  File "examples/expr2.py", line 2
    g()
  File "examples/expr2.py", line 8
    f()
  File "examples/expr2.py", line 10
    main0()
  File "examples/expr2.py", line 1
    def f():
  File "examples/expr2.py", line 10
    main0()

@certik
Copy link
Contributor

certik commented Dec 13, 2022

Awesome, very good!

I think this is pretty much it, now we just need to polish this.

Comment on lines 4976 to 4977
llvm::Value *fmt_ptr = builder->CreateGlobalStringPtr(infile);
call_print_stacktrace_addresses(context, *module, *builder, {fmt_ptr});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines should be moved below into the create_if_else branch.

@@ -1165,3 +1171,177 @@ LFORTRAN_API void _lpython_close(int64_t fd)
exit(1);
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap this into #ifdef HAVE_LFORTRAN_UNWIND

@@ -6,6 +6,9 @@
#include <inttypes.h>
#include <stdbool.h>

#include <unwind.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move into .c and wrap with #ifdef HAVE_LFORTRAN_UNWIND

@@ -6,6 +6,9 @@
#include <inttypes.h>
#include <stdbool.h>

#include <unwind.h>
#include <ctype.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to .c

@@ -18,6 +21,33 @@ struct _lfortran_complex_64 {
double re, im;
};

// Runtime Stacktrace
#define LCOMPILERS_MAX_STACKTRACE_LENGTH 200
char *binary_filename;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move into .c

Comment on lines 44 to 49
// Styles and Colors
#define DIM "\033[2m"
#define BOLD "\033[1m"
#define S_RESET "\033[0m"
#define MAGENTA "\033[35m"
#define C_RESET "\033[39m"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to .c

@Thirumalai-Shaktivel Thirumalai-Shaktivel force-pushed the runtime_stacktrace branch 3 times, most recently from 224291a to 066ea44 Compare January 12, 2023 07:40
@Thirumalai-Shaktivel Thirumalai-Shaktivel force-pushed the runtime_stacktrace branch 7 times, most recently from 72909ad to 264bbdb Compare January 12, 2023 14:47
@Thirumalai-Shaktivel
Copy link
Collaborator Author

Finally, I was able to pass all the tests.
@certik, you can now do the final review.

@Thirumalai-Shaktivel Thirumalai-Shaktivel added the ready for review PRs that are ready for review label Jan 12, 2023
@Thirumalai-Shaktivel Thirumalai-Shaktivel marked this pull request as ready for review January 12, 2023 15:33
Copy link
Contributor

@certik certik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good enough to merge. Rebase or squash the history before merging.

@Thirumalai-Shaktivel Thirumalai-Shaktivel changed the title Create runtime stacktraces Implement runtime stacktraces Jan 13, 2023
@Thirumalai-Shaktivel Thirumalai-Shaktivel merged commit a5aa754 into lcompilers:main Jan 13, 2023
@Thirumalai-Shaktivel Thirumalai-Shaktivel deleted the runtime_stacktrace branch January 13, 2023 03:47
@Thirumalai-Shaktivel
Copy link
Collaborator Author

Thanks for the approval and guidance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request ready for review PRs that are ready for review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants