JIT Interface - Issues and testing approach
Tony Simpson <[email protected]>
| Newsgroups | gmane.comp.gdb.devel |
|---|---|
| Message-ID | <CAKJTP-R_u0J6W=MnvB837KLDqR6AuPZc2Q9JDDu7J9g6Q5PmjA@mail.gmail.com> |
Hi All,
I've been having some issues with the JIT interface
[https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html]
I need to do a lot of debugging of a Python JIT I'm working on so I
wrote a plugin using the Hook API and symbol API in <gdb/jit-reader.h>
but it doesn't work, specifically (but not extensively):
Pending breakpoints on JIT functions don't trigger;
Pending breakpoints on JIT file:line don't get triggered;
Break points on JIT file:line work after symbols is registered but are
a bit weird (sorry I don't understand what's going on well enough);
The unwind and get frame id functions are never called.
I'm not certain this isn't just the limits of my understanding but one
or all of the following are true, the documentation is insufficient,
the implementation doesn't work.
I tried testing with earlier versions, on 7.4 unwind was called but
all the time which is probably not the right behaviour either, so I
think there has been some regression.
I looked at the existing tests, I'm not comfortable with TCL but it
seems to me that jit-simple.exp is not correctly testing what it says
- claims to test that symbols that move after recompilation are
updated when inferior is rerun. but it does not assert that the
symbols move and on my system they do not - so not really testing
anything, I find it difficult to workout what the other 2 jit*.exp
tests are doing but I don't think they are testing breakpoints in
JIT'd code, I could be mistaken.
I tried to put together a testcase but I'm not sure my approach is
correct, I've attached the patch - it adds files
gdb/testsuite/gdb.base/jit-fake*.* - I tried using a function as a
surrogate for JIT'd code - I thought it would be easier to maintain
than architecture specific machine code but I worry that it will cause
unrealistic conditions within GDB and also I don't know if GDB is
expected to build on compilers other than gcc which don't have label
pointers?
I'd really appreciate code review or ideas on a better approach or any feedback.
Follows is gdb session testing jit-fake and patch attached, Thanks, Tony
[tsimpson@mmpc001 gdb.base]$ ~/opt/gdb32/bin/gdb --args ./jit-fake
GNU gdb (GDB) 7.10.50.20160201-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./jit-fake...done.
(gdb) set breakpoint pending on
(gdb) jit-reader-load
/home/tsimpson/work/binutils-gdb/gdb/testsuite/gdb.base/jit-fake-reader.so
(gdb) break fakefile.c:5
No source file named fakefile.c.
Breakpoint 1 (fakefile.c:5) pending.
(gdb) break fake_function
Function "fake_function" not defined.
Breakpoint 2 (fake_function) pending.
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> fakefile.c:5
2 breakpoint keep y <PENDING> fake_function
(gdb) break main
Breakpoint 3 at 0x80486a4: file jit-fake.c, line 93.
(gdb) r
Starting program:
/home/tsimpson/work/binutils-gdb/gdb/testsuite/gdb.base/jit-fake
Breakpoint 3, main (argc=1, argv=0xffffdc34) at jit-fake.c:93
93 register_fake("fakefile.c", "fake_function", 1, 7, jit_code_fake());
(gdb) n
JIT-FAKE: jit_code_fake line_1
JIT-FAKE: jit_code_fake line_2
JIT-FAKE: jit_code_fake line_3
JIT-FAKE: jit_code_fake line_4
JIT-FAKE: jit_code_fake line_5
JIT-FAKE: jit_code_fake line_6
JIT-FAKE: jit_code_fake line_7
jit-fake-reader: jit_reader_read_debug_info being called
jit-fake-reader: open symtab for fakefile.c
jit-fake-reader: open block for fake_function 0x8048491->0x804852d
jit-fake-reader: line mapping 1 = 0x8048491
jit-fake-reader: line mapping 2 = 0x80484ab
jit-fake-reader: line mapping 3 = 0x80484c5
jit-fake-reader: line mapping 4 = 0x80484df
jit-fake-reader: line mapping 5 = 0x80484f9
jit-fake-reader: line mapping 6 = 0x8048513
jit-fake-reader: line mapping 7 = 0x804852d
jit-fake-reader: closing symtab
jit-fake-reader: closing object
jit-fake-reader: jit_reader_read_debug_info returning GDB_SUCCESS
94 jit_code_fake();
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> fakefile.c:5
2 breakpoint keep y <PENDING> fake_function
3 breakpoint keep y 0x080486a4 in main at jit-fake.c:93
breakpoint already hit 1 time
(gdb) break fakefile.c:5
Breakpoint 4 at 0x80484f9: file fakefile.c, line 5.
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> fakefile.c:5
2 breakpoint keep y <PENDING> fake_function
3 breakpoint keep y 0x080486a4 in main at jit-fake.c:93
breakpoint already hit 1 time
4 breakpoint keep y 0x080484f9 in fake_function at fakefile.c:5
(gdb) n
JIT-FAKE: jit_code_fake line_1
JIT-FAKE: jit_code_fake line_2
JIT-FAKE: jit_code_fake line_3
JIT-FAKE: jit_code_fake line_4
Breakpoint 4, fake_function () at fakefile.c:5
5 fakefile.c: No such file or directory.
(gdb) n
JIT-FAKE: jit_code_fake line_5
6 in fakefile.c
(gdb) n
JIT-FAKE: jit_code_fake line_6
0x0804852d in jit_code_fake ()
(gdb) n
Single stepping until exit from function jit_code_fake,
which has no line number information.
JIT-FAKE: jit_code_fake line_7
main (argc=1, argv=0xffffdc34) at jit-fake.c:95
95 jit_code_fake();
(gdb) n
JIT-FAKE: jit_code_fake line_1
JIT-FAKE: jit_code_fake line_2
JIT-FAKE: jit_code_fake line_3
JIT-FAKE: jit_code_fake line_4
Breakpoint 4, fake_function () at fakefile.c:5
5 fakefile.c: No such file or directory.
(gdb) c
Continuing.
JIT-FAKE: jit_code_fake line_5
JIT-FAKE: jit_code_fake line_6
JIT-FAKE: jit_code_fake line_7
JIT-FAKE: jit_code_fake line_1
JIT-FAKE: jit_code_fake line_2
JIT-FAKE: jit_code_fake line_3
JIT-FAKE: jit_code_fake line_4
Breakpoint 4, fake_function () at fakefile.c:5
5 in fakefile.c
(gdb) c
Continuing.
JIT-FAKE: jit_code_fake line_5
JIT-FAKE: jit_code_fake line_6
JIT-FAKE: jit_code_fake line_7
[Inferior 1 (process 14370) exited normally]
jit-fake.patch
(text/x-patch, 8.8 KB)
diff --git gdb/testsuite/gdb.base/jit-fake-code.c gdb/testsuite/gdb.base/jit-fake-code.c
new file mode 100644
index 0000000..c3c5183
--- /dev/null
+++ gdb/testsuite/gdb.base/jit-fake-code.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+
+/* BUILD: gcc -s -c jit-fake-code.c */
+
+void ** jit_code_fake(void) {
+ static void *addresses[7];
+ line_1:
+ printf("jit-fake: jit_code_fake line_1\n");
+ addresses[0] = &&line_1;
+ line_2:
+ printf("jit-fake: jit_code_fake line_2\n");
+ addresses[1] = &&line_2;
+ line_3:
+ printf("jit-fake: jit_code_fake line_3\n");
+ addresses[2] = &&line_3;
+ line_4:
+ printf("jit-fake: jit_code_fake line_4\n");
+ addresses[3] = &&line_4;
+ line_5:
+ printf("jit-fake: jit_code_fake line_5\n");
+ addresses[4] = &&line_5;
+ line_6:
+ printf("jit-fake: jit_code_fake line_6\n");
+ addresses[5] = &&line_6;
+ line_7:
+ printf("jit-fake: jit_code_fake line_7\n");
+ addresses[6] = &&line_7;
+ return addresses;
+}
\ No newline at end of file
diff --git gdb/testsuite/gdb.base/jit-fake-reader.c gdb/testsuite/gdb.base/jit-fake-reader.c
new file mode 100644
index 0000000..96f6626
--- /dev/null
+++ gdb/testsuite/gdb.base/jit-fake-reader.c
@@ -0,0 +1,105 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <gdb/jit-reader.h>
+
+#include "jit-fake.h"
+
+/*
+ * BUILD: gcc -g -fPIC -shared -o jit-fake-reader.so jit-fake-reader.c
+ *
+ * load in gdb:
+ * jit-reader-load [YOUR SOURCE LOCATION]/gdb/testsuite/gdb.base/jit-fake-reader.so
+ */
+
+/*
+ * Required for gdb License compliance, this code is MIT and therefore
+ * GPL compatible.
+ */
+GDB_DECLARE_GPL_COMPATIBLE_READER
+
+
+enum gdb_status jit_reader_read_debug_info(struct gdb_reader_funcs *self,
+ struct gdb_symbol_callbacks *cb,
+ void *memory, long memory_sz) {
+ struct gdb_object * obj;
+ struct gdb_symtab *symtab;
+ symbol_data_t * data;
+
+ printf("jit-fake-reader: jit_reader_read_debug_info being called\n");
+ data = (symbol_data_t *)memory;
+ obj = cb->object_open(cb);
+ printf("jit-fake-reader: open symtab for %s\n", data->filename);
+ symtab = cb->symtab_open(cb, obj, data->filename);
+ {
+ void * start_address = data->line_addresses[0];
+ void * end_address = data->line_addresses[data->num_lines-1];
+ printf("jit-fake-reader: open block for %s %p->%p\n",
+ data->func_name,
+ start_address,
+ end_address);
+ cb->block_open(cb, symtab, NULL, (unsigned long)start_address,
+ (unsigned long)end_address, data->func_name);
+ }
+ {
+ int i;
+ struct gdb_line_mapping *line_mappings;
+ line_mappings = (struct gdb_line_mapping *)calloc(
+ data->num_lines,
+ sizeof(struct gdb_line_mapping));
+ for(i = 0; i < data->num_lines; i++)
+ {
+ line_mappings[i].line = data->line_offset + i;
+ line_mappings[i].pc =
+ (unsigned long)data->line_addresses[i];
+ printf("jit-fake-reader: line mapping %d = %p\n",
+ line_mappings[i].line,
+ (void *)line_mappings[i].pc);
+ }
+ cb->line_mapping_add(cb, symtab, data->num_lines,
+ line_mappings);
+ free(line_mappings);
+ }
+ printf("jit-fake-reader: closing symtab\n");
+ cb->symtab_close(cb, symtab);
+ printf("jit-fake-reader: closing object\n");
+ cb->object_close(cb, obj);
+ printf("jit-fake-reader: jit_reader_read_debug_info returning "
+ "GDB_SUCCESS\n");
+ return GDB_SUCCESS;
+}
+
+
+void jit_reader_destroy_reader(struct gdb_reader_funcs *self) {
+ printf("jit-fake-reader: jit_reader_destroy_reader being called\n");
+}
+
+struct gdb_frame_id jit_reader_get_frame_id(struct gdb_reader_funcs *self,
+ struct gdb_unwind_callbacks *c)
+{
+ static struct gdb_frame_id id;
+ printf("jit-fake-reader: jit_reader_get_frame_id being called\n");
+ id.code_address = 0;
+ id.stack_address = 0;
+ return id;
+}
+
+
+enum gdb_status jit_reader_unwind_frame(struct gdb_reader_funcs *self,
+ struct gdb_unwind_callbacks *cb)
+{
+ printf("jit-fake-reader: jit_reader_unwind_frame being called\n");
+ return GDB_FAIL;
+}
+
+
+static struct gdb_reader_funcs jit_reader_funcs;
+
+
+struct gdb_reader_funcs *gdb_init_reader (void) {
+ jit_reader_funcs.reader_version = GDB_READER_INTERFACE_VERSION;
+ jit_reader_funcs.read = &jit_reader_read_debug_info;
+ jit_reader_funcs.unwind = &jit_reader_unwind_frame;
+ jit_reader_funcs.get_frame_id = &jit_reader_get_frame_id;
+ jit_reader_funcs.destroy = &jit_reader_destroy_reader;
+ return &jit_reader_funcs;
+}
\ No newline at end of file
diff --git gdb/testsuite/gdb.base/jit-fake.c gdb/testsuite/gdb.base/jit-fake.c
new file mode 100644
index 0000000..edae835
--- /dev/null
+++ gdb/testsuite/gdb.base/jit-fake.c
@@ -0,0 +1,98 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "jit-fake.h"
+
+/* BUILD: gcc -g jit-fake-code.o jit-fake.c -o jit-fake */
+/******************************************/
+/**** Start GDB JIT Hook Specific Bits ****/
+/******************************************/
+typedef enum
+{
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+} jit_actions_t;
+
+struct jit_code_entry
+{
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+};
+
+struct jit_descriptor
+{
+ uint32_t version;
+ /* This type should be jit_actions_t, but we use uint32_t
+ to be explicit about the bitwidth. */
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+};
+
+void __attribute__((noinline)) __jit_debug_register_code() { };
+
+struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 };
+/****************************************/
+/**** End GDB JIT Hook Specific Bits ****/
+/****************************************/
+
+
+void register_symbol_data(void *symfile_addr, size_t symfile_size)
+{
+ struct jit_code_entry *entry = malloc(sizeof(struct jit_code_entry));
+ entry->symfile_addr = symfile_addr;
+ entry->symfile_size = symfile_size;
+ entry->prev_entry = NULL;
+ entry->next_entry = __jit_debug_descriptor.first_entry;
+ if(entry->next_entry != NULL)
+ {
+ entry->next_entry->prev_entry = entry;
+ }
+ __jit_debug_descriptor.first_entry = entry;
+ __jit_debug_descriptor.relevant_entry = entry;
+ __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
+ __jit_debug_register_code();
+}
+
+
+symbol_data_t * create_symbol_data(char *filename, char *func_name,
+ int line_offset, int num_lines,
+ void ** line_addresses)
+{
+ symbol_data_t *symbol_data = malloc(sizeof(symbol_data_t));
+ strncpy(symbol_data->filename, filename, SYMBOL_DATA_MAX_STR_LEN);
+ strncpy(symbol_data->func_name, func_name, SYMBOL_DATA_MAX_STR_LEN);
+ symbol_data->line_offset = line_offset;
+ symbol_data->num_lines = num_lines;
+ memcpy(symbol_data->line_addresses, line_addresses,
+ sizeof(void *) * num_lines);
+ return symbol_data;
+}
+
+
+void register_fake(char *filename, char *func_name, int line_offset,
+ int num_lines, void * line_addresses[])
+{
+ symbol_data_t *symbol_data = create_symbol_data(filename, func_name,
+ line_offset,
+ num_lines,
+ line_addresses);
+
+ register_symbol_data(symbol_data, sizeof(symbol_data_t));
+}
+
+/* provided by jit-fake-code.so from jit-fake-code.c */
+void ** jit_code_fake(void);
+
+int main(int argc, char **argv) {
+ register_fake("fakefile.c", "fake_function", 1, 7, jit_code_fake());
+ jit_code_fake();
+ jit_code_fake();
+ jit_code_fake();
+ return 0;
+}
\ No newline at end of file
diff --git gdb/testsuite/gdb.base/jit-fake.h gdb/testsuite/gdb.base/jit-fake.h
new file mode 100644
index 0000000..5f37a42
--- /dev/null
+++ gdb/testsuite/gdb.base/jit-fake.h
@@ -0,0 +1,15 @@
+#ifndef JIT_FAKE_H
+#define JIT_FAKE_H
+
+#define SYMBOL_DATA_MAX_STR_LEN 2048
+#define SYMBOL_DATA_MAX_LINES 20
+
+typedef struct {
+ char filename[SYMBOL_DATA_MAX_STR_LEN];
+ char func_name[SYMBOL_DATA_MAX_STR_LEN];
+ int line_offset;
+ int num_lines;
+ void *line_addresses[SYMBOL_DATA_MAX_LINES];
+} symbol_data_t;
+
+#endif /* JIT_FAKE_H */
\ No newline at end of file