-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathtracepoint.cc
6513 lines (5347 loc) · 178 KB
/
tracepoint.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Tracepoint code for remote server for GDB.
Copyright (C) 2009-2024 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "tracepoint.h"
#include "gdbthread.h"
#include "gdbsupport/rsp-low.h"
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <chrono>
#include <inttypes.h>
#include "ax.h"
#include "tdesc.h"
#define IPA_SYM_STRUCT_NAME ipa_sym_addresses
#include "gdbsupport/agent.h"
#define DEFAULT_TRACE_BUFFER_SIZE 5242880 /* 5*1024*1024 */
/* This file is built for both GDBserver, and the in-process
agent (IPA), a shared library that includes a tracing agent that is
loaded by the inferior to support fast tracepoints. Fast
tracepoints (or more accurately, jump based tracepoints) are
implemented by patching the tracepoint location with a jump into a
small trampoline function whose job is to save the register state,
call the in-process tracing agent, and then execute the original
instruction that was under the tracepoint jump (possibly adjusted,
if PC-relative, or some such).
The current synchronization design is pull based. That means,
GDBserver does most of the work, by peeking/poking at the inferior
agent's memory directly for downloading tracepoint and associated
objects, and for uploading trace frames. Whenever the IPA needs
something from GDBserver (trace buffer is full, tracing stopped for
some reason, etc.) the IPA calls a corresponding hook function
where GDBserver has placed a breakpoint.
Each of the agents has its own trace buffer. When browsing the
trace frames built from slow and fast tracepoints from GDB (tfind
mode), there's no guarantee the user is seeing the trace frames in
strict chronological creation order, although, GDBserver tries to
keep the order relatively reasonable, by syncing the trace buffers
at appropriate times.
*/
#ifdef IN_PROCESS_AGENT
static void trace_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
static void
trace_vdebug (const char *fmt, ...)
{
char buf[1024];
va_list ap;
va_start (ap, fmt);
vsprintf (buf, fmt, ap);
fprintf (stderr, PROG "/tracepoint: %s\n", buf);
va_end (ap);
}
#define trace_debug(fmt, args...) \
do { \
if (debug_threads) \
trace_vdebug ((fmt), ##args); \
} while (0)
#else
#define trace_debug(fmt, args...) \
do { \
threads_debug_printf ((fmt), ##args); \
} while (0)
#endif
/* Prefix exported symbols, for good citizenship. All the symbols
that need exporting are defined in this module. Note that all
these symbols must be tagged with IP_AGENT_EXPORT_*. */
#ifdef IN_PROCESS_AGENT
# define gdb_tp_heap_buffer IPA_SYM_EXPORTED_NAME (gdb_tp_heap_buffer)
# define gdb_jump_pad_buffer IPA_SYM_EXPORTED_NAME (gdb_jump_pad_buffer)
# define gdb_jump_pad_buffer_end IPA_SYM_EXPORTED_NAME (gdb_jump_pad_buffer_end)
# define gdb_trampoline_buffer IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer)
# define gdb_trampoline_buffer_end IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer_end)
# define gdb_trampoline_buffer_error IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer_error)
# define collecting IPA_SYM_EXPORTED_NAME (collecting)
# define gdb_collect_ptr IPA_SYM_EXPORTED_NAME (gdb_collect_ptr)
# define stop_tracing IPA_SYM_EXPORTED_NAME (stop_tracing)
# define flush_trace_buffer IPA_SYM_EXPORTED_NAME (flush_trace_buffer)
# define about_to_request_buffer_space IPA_SYM_EXPORTED_NAME (about_to_request_buffer_space)
# define trace_buffer_is_full IPA_SYM_EXPORTED_NAME (trace_buffer_is_full)
# define stopping_tracepoint IPA_SYM_EXPORTED_NAME (stopping_tracepoint)
# define expr_eval_result IPA_SYM_EXPORTED_NAME (expr_eval_result)
# define error_tracepoint IPA_SYM_EXPORTED_NAME (error_tracepoint)
# define tracepoints IPA_SYM_EXPORTED_NAME (tracepoints)
# define tracing IPA_SYM_EXPORTED_NAME (tracing)
# define trace_buffer_ctrl IPA_SYM_EXPORTED_NAME (trace_buffer_ctrl)
# define trace_buffer_ctrl_curr IPA_SYM_EXPORTED_NAME (trace_buffer_ctrl_curr)
# define trace_buffer_lo IPA_SYM_EXPORTED_NAME (trace_buffer_lo)
# define trace_buffer_hi IPA_SYM_EXPORTED_NAME (trace_buffer_hi)
# define traceframe_read_count IPA_SYM_EXPORTED_NAME (traceframe_read_count)
# define traceframe_write_count IPA_SYM_EXPORTED_NAME (traceframe_write_count)
# define traceframes_created IPA_SYM_EXPORTED_NAME (traceframes_created)
# define trace_state_variables IPA_SYM_EXPORTED_NAME (trace_state_variables)
# define get_raw_reg_ptr IPA_SYM_EXPORTED_NAME (get_raw_reg_ptr)
# define get_trace_state_variable_value_ptr \
IPA_SYM_EXPORTED_NAME (get_trace_state_variable_value_ptr)
# define set_trace_state_variable_value_ptr \
IPA_SYM_EXPORTED_NAME (set_trace_state_variable_value_ptr)
# define helper_thread_id IPA_SYM_EXPORTED_NAME (helper_thread_id)
# define cmd_buf IPA_SYM_EXPORTED_NAME (cmd_buf)
# define ipa_tdesc_idx IPA_SYM_EXPORTED_NAME (ipa_tdesc_idx)
#endif
#ifndef IN_PROCESS_AGENT
/* Addresses of in-process agent's symbols GDBserver cares about. */
struct ipa_sym_addresses
{
CORE_ADDR addr_gdb_tp_heap_buffer;
CORE_ADDR addr_gdb_jump_pad_buffer;
CORE_ADDR addr_gdb_jump_pad_buffer_end;
CORE_ADDR addr_gdb_trampoline_buffer;
CORE_ADDR addr_gdb_trampoline_buffer_end;
CORE_ADDR addr_gdb_trampoline_buffer_error;
CORE_ADDR addr_collecting;
CORE_ADDR addr_gdb_collect_ptr;
CORE_ADDR addr_stop_tracing;
CORE_ADDR addr_flush_trace_buffer;
CORE_ADDR addr_about_to_request_buffer_space;
CORE_ADDR addr_trace_buffer_is_full;
CORE_ADDR addr_stopping_tracepoint;
CORE_ADDR addr_expr_eval_result;
CORE_ADDR addr_error_tracepoint;
CORE_ADDR addr_tracepoints;
CORE_ADDR addr_tracing;
CORE_ADDR addr_trace_buffer_ctrl;
CORE_ADDR addr_trace_buffer_ctrl_curr;
CORE_ADDR addr_trace_buffer_lo;
CORE_ADDR addr_trace_buffer_hi;
CORE_ADDR addr_traceframe_read_count;
CORE_ADDR addr_traceframe_write_count;
CORE_ADDR addr_traceframes_created;
CORE_ADDR addr_trace_state_variables;
CORE_ADDR addr_get_raw_reg_ptr;
CORE_ADDR addr_get_trace_state_variable_value_ptr;
CORE_ADDR addr_set_trace_state_variable_value_ptr;
CORE_ADDR addr_ipa_tdesc_idx;
};
static struct
{
const char *name;
int offset;
} symbol_list[] = {
IPA_SYM(gdb_tp_heap_buffer),
IPA_SYM(gdb_jump_pad_buffer),
IPA_SYM(gdb_jump_pad_buffer_end),
IPA_SYM(gdb_trampoline_buffer),
IPA_SYM(gdb_trampoline_buffer_end),
IPA_SYM(gdb_trampoline_buffer_error),
IPA_SYM(collecting),
IPA_SYM(gdb_collect_ptr),
IPA_SYM(stop_tracing),
IPA_SYM(flush_trace_buffer),
IPA_SYM(about_to_request_buffer_space),
IPA_SYM(trace_buffer_is_full),
IPA_SYM(stopping_tracepoint),
IPA_SYM(expr_eval_result),
IPA_SYM(error_tracepoint),
IPA_SYM(tracepoints),
IPA_SYM(tracing),
IPA_SYM(trace_buffer_ctrl),
IPA_SYM(trace_buffer_ctrl_curr),
IPA_SYM(trace_buffer_lo),
IPA_SYM(trace_buffer_hi),
IPA_SYM(traceframe_read_count),
IPA_SYM(traceframe_write_count),
IPA_SYM(traceframes_created),
IPA_SYM(trace_state_variables),
IPA_SYM(get_raw_reg_ptr),
IPA_SYM(get_trace_state_variable_value_ptr),
IPA_SYM(set_trace_state_variable_value_ptr),
IPA_SYM(ipa_tdesc_idx),
};
static struct ipa_sym_addresses ipa_sym_addrs;
static int read_inferior_integer (CORE_ADDR symaddr, int *val);
static void
write_e_ipa_not_loaded (char *buffer)
{
sprintf (buffer,
"E.In-process agent library not loaded in process. "
"Fast tracepoints unavailable.");
}
/* If the in-process agent library isn't loaded in the inferior, write
an error to BUFFER, and return 1. Otherwise, return 0. */
static int
maybe_write_ipa_not_loaded (char *buffer)
{
if (!agent_loaded_p ())
{
write_e_ipa_not_loaded (buffer);
return 1;
}
return 0;
}
/* Cache all future symbols that the tracepoints module might request.
We can not request symbols at arbitrary states in the remote
protocol, only when the client tells us that new symbols are
available. So when we load the in-process library, make sure to
check the entire list. */
void
tracepoint_look_up_symbols (void)
{
int i;
if (agent_loaded_p ())
return;
for (i = 0; i < sizeof (symbol_list) / sizeof (symbol_list[0]); i++)
{
CORE_ADDR *addrp =
(CORE_ADDR *) ((char *) &ipa_sym_addrs + symbol_list[i].offset);
if (look_up_one_symbol (symbol_list[i].name, addrp, 1) == 0)
{
threads_debug_printf ("symbol `%s' not found", symbol_list[i].name);
return;
}
}
agent_look_up_symbols (NULL);
}
#endif
/* GDBserver places a breakpoint on the IPA's version (which is a nop)
of the "stop_tracing" function. When this breakpoint is hit,
tracing stopped in the IPA for some reason. E.g., due to
tracepoint reaching the pass count, hitting conditional expression
evaluation error, etc.
The IPA's trace buffer is never in circular tracing mode: instead,
GDBserver's is, and whenever the in-process buffer fills, it calls
"flush_trace_buffer", which triggers an internal breakpoint.
GDBserver reacts to this breakpoint by pulling the meanwhile
collected data. Old frames discarding is always handled on the
GDBserver side. */
#ifdef IN_PROCESS_AGENT
/* See target.h. */
int
read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
memcpy (myaddr, (void *) (uintptr_t) memaddr, len);
return 0;
}
/* Call this in the functions where GDBserver places a breakpoint, so
that the compiler doesn't try to be clever and skip calling the
function at all. This is necessary, even if we tell the compiler
to not inline said functions. */
#if defined(__GNUC__)
# define UNKNOWN_SIDE_EFFECTS() asm ("")
#else
# define UNKNOWN_SIDE_EFFECTS() do {} while (0)
#endif
/* This is needed for -Wmissing-declarations. */
IP_AGENT_EXPORT_FUNC void stop_tracing (void);
IP_AGENT_EXPORT_FUNC void
stop_tracing (void)
{
/* GDBserver places breakpoint here. */
UNKNOWN_SIDE_EFFECTS();
}
/* This is needed for -Wmissing-declarations. */
IP_AGENT_EXPORT_FUNC void flush_trace_buffer (void);
IP_AGENT_EXPORT_FUNC void
flush_trace_buffer (void)
{
/* GDBserver places breakpoint here. */
UNKNOWN_SIDE_EFFECTS();
}
#endif
#ifndef IN_PROCESS_AGENT
static int
tracepoint_handler (CORE_ADDR address)
{
trace_debug ("tracepoint_handler: tracepoint at 0x%s hit",
paddress (address));
return 0;
}
/* Breakpoint at "stop_tracing" in the inferior lib. */
static struct breakpoint *stop_tracing_bkpt;
static int stop_tracing_handler (CORE_ADDR);
/* Breakpoint at "flush_trace_buffer" in the inferior lib. */
static struct breakpoint *flush_trace_buffer_bkpt;
static int flush_trace_buffer_handler (CORE_ADDR);
static void download_trace_state_variables (void);
static void upload_fast_traceframes (void);
static int run_inferior_command (char *cmd, int len);
static int
read_inferior_integer (CORE_ADDR symaddr, int *val)
{
return read_inferior_memory (symaddr, (unsigned char *) val,
sizeof (*val));
}
struct tracepoint;
static int tracepoint_send_agent (struct tracepoint *tpoint);
static int
read_inferior_uinteger (CORE_ADDR symaddr, unsigned int *val)
{
return read_inferior_memory (symaddr, (unsigned char *) val,
sizeof (*val));
}
static int
read_inferior_data_pointer (CORE_ADDR symaddr, CORE_ADDR *val)
{
void *pval = (void *) (uintptr_t) val;
int ret;
ret = read_inferior_memory (symaddr, (unsigned char *) &pval, sizeof (pval));
*val = (uintptr_t) pval;
return ret;
}
static int
write_inferior_data_pointer (CORE_ADDR symaddr, CORE_ADDR val)
{
void *pval = (void *) (uintptr_t) val;
return target_write_memory (symaddr,
(unsigned char *) &pval, sizeof (pval));
}
static int
write_inferior_integer (CORE_ADDR symaddr, int val)
{
return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
}
static int
write_inferior_int8 (CORE_ADDR symaddr, int8_t val)
{
return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
}
static int
write_inferior_uinteger (CORE_ADDR symaddr, unsigned int val)
{
return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
}
static CORE_ADDR target_malloc (ULONGEST size);
#define COPY_FIELD_TO_BUF(BUF, OBJ, FIELD) \
do { \
memcpy (BUF, &(OBJ)->FIELD, sizeof ((OBJ)->FIELD)); \
BUF += sizeof ((OBJ)->FIELD); \
} while (0)
#endif
/* Base action. Concrete actions inherit this. */
struct tracepoint_action
{
char type;
};
/* An 'M' (collect memory) action. */
struct collect_memory_action
{
struct tracepoint_action base;
ULONGEST addr;
ULONGEST len;
int32_t basereg;
};
/* An 'R' (collect registers) action. */
struct collect_registers_action
{
struct tracepoint_action base;
};
/* An 'X' (evaluate expression) action. */
struct eval_expr_action
{
struct tracepoint_action base;
struct agent_expr *expr;
};
#ifndef IN_PROCESS_AGENT
static CORE_ADDR
m_tracepoint_action_download (const struct tracepoint_action *action)
{
CORE_ADDR ipa_action = target_malloc (sizeof (struct collect_memory_action));
target_write_memory (ipa_action, (unsigned char *) action,
sizeof (struct collect_memory_action));
return ipa_action;
}
static char *
m_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
{
struct collect_memory_action *maction
= (struct collect_memory_action *) action;
COPY_FIELD_TO_BUF (buffer, maction, addr);
COPY_FIELD_TO_BUF (buffer, maction, len);
COPY_FIELD_TO_BUF (buffer, maction, basereg);
return buffer;
}
static CORE_ADDR
r_tracepoint_action_download (const struct tracepoint_action *action)
{
CORE_ADDR ipa_action = target_malloc (sizeof (struct collect_registers_action));
target_write_memory (ipa_action, (unsigned char *) action,
sizeof (struct collect_registers_action));
return ipa_action;
}
static char *
r_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
{
return buffer;
}
static CORE_ADDR download_agent_expr (struct agent_expr *expr);
static CORE_ADDR
x_tracepoint_action_download (const struct tracepoint_action *action)
{
CORE_ADDR ipa_action = target_malloc (sizeof (struct eval_expr_action));
CORE_ADDR expr;
target_write_memory (ipa_action, (unsigned char *) action,
sizeof (struct eval_expr_action));
expr = download_agent_expr (((struct eval_expr_action *) action)->expr);
write_inferior_data_pointer (ipa_action
+ offsetof (struct eval_expr_action, expr),
expr);
return ipa_action;
}
/* Copy agent expression AEXPR to buffer pointed by P. If AEXPR is NULL,
copy 0 to P. Return updated header of buffer. */
static char *
agent_expr_send (char *p, const struct agent_expr *aexpr)
{
/* Copy the length of condition first, and then copy its
content. */
if (aexpr == NULL)
{
memset (p, 0, 4);
p += 4;
}
else
{
memcpy (p, &aexpr->length, 4);
p +=4;
memcpy (p, aexpr->bytes, aexpr->length);
p += aexpr->length;
}
return p;
}
static char *
x_tracepoint_action_send ( char *buffer, const struct tracepoint_action *action)
{
struct eval_expr_action *eaction = (struct eval_expr_action *) action;
return agent_expr_send (buffer, eaction->expr);
}
static char *
tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
{
switch (action->type)
{
case 'M':
return m_tracepoint_action_send (buffer, action);
case 'R':
return r_tracepoint_action_send (buffer, action);
case 'X':
return x_tracepoint_action_send (buffer, action);
}
error ("Unknown trace action '%c'.", action->type);
}
static CORE_ADDR
tracepoint_action_download (const struct tracepoint_action *action)
{
switch (action->type)
{
case 'M':
return m_tracepoint_action_download (action);
case 'R':
return r_tracepoint_action_download (action);
case 'X':
return x_tracepoint_action_download (action);
}
error ("Unknown trace action '%c'.", action->type);
}
#endif
/* This structure describes a piece of the source-level definition of
the tracepoint. The contents are not interpreted by the target,
but preserved verbatim for uploading upon reconnection. */
struct source_string
{
/* The type of string, such as "cond" for a conditional. */
char *type;
/* The source-level string itself. For the sake of target
debugging, we store it in plaintext, even though it is always
transmitted in hex. */
char *str;
/* Link to the next one in the list. We link them in the order
received, in case some make up an ordered list of commands or
some such. */
struct source_string *next;
};
enum tracepoint_type
{
/* Trap based tracepoint. */
trap_tracepoint,
/* A fast tracepoint implemented with a jump instead of a trap. */
fast_tracepoint,
};
typedef enum eval_result_type (*condfn) (unsigned char *,
ULONGEST *);
/* The definition of a tracepoint. */
/* Tracepoints may have multiple locations, each at a different
address. This can occur with optimizations, template
instantiation, etc. Since the locations may be in different
scopes, the conditions and actions may be different for each
location. Our target version of tracepoints is more like GDB's
notion of "breakpoint locations", but we have almost nothing that
is not per-location, so we bother having two kinds of objects. The
key consequence is that numbers are not unique, and that it takes
both number and address to identify a tracepoint uniquely. */
struct tracepoint
{
/* The number of the tracepoint, as specified by GDB. Several
tracepoint objects here may share a number. */
uint32_t number;
/* Address at which the tracepoint is supposed to trigger. Several
tracepoints may share an address. */
CORE_ADDR address;
/* Tracepoint type. */
enum tracepoint_type type;
/* True if the tracepoint is currently enabled. */
int8_t enabled;
/* The number of single steps that will be performed after each
tracepoint hit. */
uint64_t step_count;
/* The number of times the tracepoint may be hit before it will
terminate the entire tracing run. */
uint64_t pass_count;
/* Pointer to the agent expression that is the tracepoint's
conditional, or NULL if the tracepoint is unconditional. */
struct agent_expr *cond;
/* The list of actions to take when the tracepoint triggers. */
uint32_t numactions;
struct tracepoint_action **actions;
/* Count of the times we've hit this tracepoint during the run.
Note that while-stepping steps are not counted as "hits". */
uint64_t hit_count;
/* Cached sum of the sizes of traceframes created by this point. */
uint64_t traceframe_usage;
CORE_ADDR compiled_cond;
/* Link to the next tracepoint in the list. */
struct tracepoint *next;
#ifndef IN_PROCESS_AGENT
/* The list of actions to take when the tracepoint triggers, in
string/packet form. */
char **actions_str;
/* The collection of strings that describe the tracepoint as it was
entered into GDB. These are not used by the target, but are
reported back to GDB upon reconnection. */
struct source_string *source_strings;
/* The number of bytes displaced by fast tracepoints. It may subsume
multiple instructions, for multi-byte fast tracepoints. This
field is only valid for fast tracepoints. */
uint32_t orig_size;
/* Only for fast tracepoints. */
CORE_ADDR obj_addr_on_target;
/* Address range where the original instruction under a fast
tracepoint was relocated to. (_end is actually one byte past
the end). */
CORE_ADDR adjusted_insn_addr;
CORE_ADDR adjusted_insn_addr_end;
/* The address range of the piece of the jump pad buffer that was
assigned to this fast tracepoint. (_end is actually one byte
past the end).*/
CORE_ADDR jump_pad;
CORE_ADDR jump_pad_end;
/* The address range of the piece of the trampoline buffer that was
assigned to this fast tracepoint. (_end is actually one byte
past the end). */
CORE_ADDR trampoline;
CORE_ADDR trampoline_end;
/* The list of actions to take while in a stepping loop. These
fields are only valid for patch-based tracepoints. */
int num_step_actions;
struct tracepoint_action **step_actions;
/* Same, but in string/packet form. */
char **step_actions_str;
/* Handle returned by the breakpoint or tracepoint module when we
inserted the trap or jump.
NULL if we haven't inserted it yet. */
void *handle;
#endif
};
#ifndef IN_PROCESS_AGENT
/* Given `while-stepping', a thread may be collecting data for more
than one tracepoint simultaneously. On the other hand, the same
tracepoint with a while-stepping action may be hit by more than one
thread simultaneously (but not quite, each thread could be handling
a different step). Each thread holds a list of these objects,
representing the current step of each while-stepping action being
collected. */
struct wstep_state
{
struct wstep_state *next;
/* The tracepoint number. */
int tp_number;
/* The tracepoint's address. */
CORE_ADDR tp_address;
/* The number of the current step in this 'while-stepping'
action. */
long current_step;
};
#endif
extern "C" {
/* The linked list of all tracepoints. Marked explicitly as used as
the in-process library doesn't use it for the fast tracepoints
support. */
IP_AGENT_EXPORT_VAR struct tracepoint *tracepoints;
/* The first tracepoint to exceed its pass count. */
IP_AGENT_EXPORT_VAR struct tracepoint *stopping_tracepoint;
/* True if the trace buffer is full or otherwise no longer usable. */
IP_AGENT_EXPORT_VAR int trace_buffer_is_full;
/* The first error that occurred during expression evaluation. */
/* Stored as an int to avoid the IPA ABI being dependent on whatever
the compiler decides to use for the enum's underlying type. Holds
enum eval_result_type values. */
IP_AGENT_EXPORT_VAR int expr_eval_result = expr_eval_no_error;
}
#ifndef IN_PROCESS_AGENT
/* Pointer to the last tracepoint in the list, new tracepoints are
linked in at the end. */
static struct tracepoint *last_tracepoint;
static const char * const eval_result_names[] =
{
#define AX_RESULT_TYPE(ENUM,STR) STR,
#include "ax-result-types.def"
#undef AX_RESULT_TYPE
};
#endif
/* The tracepoint in which the error occurred. */
extern "C" {
IP_AGENT_EXPORT_VAR struct tracepoint *error_tracepoint;
}
struct trace_state_variable
{
/* This is the name of the variable as used in GDB. The target
doesn't use the name, but needs to have it for saving and
reconnection purposes. */
char *name;
/* This number identifies the variable uniquely. Numbers may be
assigned either by the target (in the case of builtin variables),
or by GDB, and are presumed unique during the course of a trace
experiment. */
int number;
/* The variable's initial value, a 64-bit signed integer always. */
LONGEST initial_value;
/* The variable's value, a 64-bit signed integer always. */
LONGEST value;
/* Pointer to a getter function, used to supply computed values. */
LONGEST (*getter) (void);
/* Link to the next variable. */
struct trace_state_variable *next;
};
/* Linked list of all trace state variables. */
#ifdef IN_PROCESS_AGENT
static struct trace_state_variable *alloced_trace_state_variables;
#endif
IP_AGENT_EXPORT_VAR struct trace_state_variable *trace_state_variables;
/* The results of tracing go into a fixed-size space known as the
"trace buffer". Because usage follows a limited number of
patterns, we manage it ourselves rather than with malloc. Basic
rules are that we create only one trace frame at a time, each is
variable in size, they are never moved once created, and we only
discard if we are doing a circular buffer, and then only the oldest
ones. Each trace frame includes its own size, so we don't need to
link them together, and the trace frame number is relative to the
first one, so we don't need to record numbers. A trace frame also
records the number of the tracepoint that created it. The data
itself is a series of blocks, each introduced by a single character
and with a defined format. Each type of block has enough
type/length info to allow scanners to jump quickly from one block
to the next without reading each byte in the block. */
/* Trace buffer management would be simple - advance a free pointer
from beginning to end, then stop - were it not for the circular
buffer option, which is a useful way to prevent a trace run from
stopping prematurely because the buffer filled up. In the circular
case, the location of the first trace frame (trace_buffer_start)
moves as old trace frames are discarded. Also, since we grow trace
frames incrementally as actions are performed, we wrap around to
the beginning of the trace buffer. This is per-block, so each
block within a trace frame remains contiguous. Things get messy
when the wrapped-around trace frame is the one being discarded; the
free space ends up in two parts at opposite ends of the buffer. */
#ifndef ATTR_PACKED
# if defined(__GNUC__)
# define ATTR_PACKED __attribute__ ((packed))
# else
# define ATTR_PACKED /* nothing */
# endif
#endif
/* The data collected at a tracepoint hit. This object should be as
small as possible, since there may be a great many of them. We do
not need to keep a frame number, because they are all sequential
and there are no deletions; so the Nth frame in the buffer is
always frame number N. */
struct traceframe
{
/* Number of the tracepoint that collected this traceframe. A value
of 0 indicates the current end of the trace buffer. We make this
a 16-bit field because it's never going to happen that GDB's
numbering of tracepoints reaches 32,000. */
int tpnum : 16;
/* The size of the data in this trace frame. We limit this to 32
bits, even on a 64-bit target, because it's just implausible that
one is validly going to collect 4 gigabytes of data at a single
tracepoint hit. */
unsigned int data_size : 32;
/* The base of the trace data, which is contiguous from this point. */
unsigned char data[0];
} ATTR_PACKED;
/* The size of the EOB marker, in bytes. A traceframe with zeroed
fields (and no data) marks the end of trace data. */
#define TRACEFRAME_EOB_MARKER_SIZE offsetof (struct traceframe, data)
/* This flag is true if the trace buffer is circular, meaning that
when it fills, the oldest trace frames are discarded in order to
make room. */
#ifndef IN_PROCESS_AGENT
static int circular_trace_buffer;
#endif
/* Size of the trace buffer. */
static LONGEST trace_buffer_size;
extern "C" {
/* Pointer to the block of memory that traceframes all go into. */
IP_AGENT_EXPORT_VAR unsigned char *trace_buffer_lo;
/* Pointer to the end of the trace buffer, more precisely to the byte
after the end of the buffer. */
IP_AGENT_EXPORT_VAR unsigned char *trace_buffer_hi;
}
/* Control structure holding the read/write/etc. pointers into the
trace buffer. We need more than one of these to implement a
transaction-like mechanism to guarantees that both GDBserver and the
in-process agent can try to change the trace buffer
simultaneously. */
struct trace_buffer_control
{
/* Pointer to the first trace frame in the buffer. In the
non-circular case, this is equal to trace_buffer_lo, otherwise it
moves around in the buffer. */
unsigned char *start;
/* Pointer to the free part of the trace buffer. Note that we clear
several bytes at and after this pointer, so that traceframe
scans/searches terminate properly. */
unsigned char *free;
/* Pointer to the byte after the end of the free part. Note that
this may be smaller than trace_buffer_free in the circular case,
and means that the free part is in two pieces. Initially it is
equal to trace_buffer_hi, then is generally equivalent to
trace_buffer_start. */
unsigned char *end_free;
/* Pointer to the wraparound. If not equal to trace_buffer_hi, then
this is the point at which the trace data breaks, and resumes at
trace_buffer_lo. */
unsigned char *wrap;
};
/* Same as above, to be used by GDBserver when updating the in-process
agent. */
struct ipa_trace_buffer_control
{
uintptr_t start;
uintptr_t free;
uintptr_t end_free;
uintptr_t wrap;
};
/* We have possibly both GDBserver and an inferior thread accessing
the same IPA trace buffer memory. The IPA is the producer (tries
to put new frames in the buffer), while GDBserver occasionally
consumes them, that is, flushes the IPA's buffer into its own
buffer. Both sides need to update the trace buffer control
pointers (current head, tail, etc.). We can't use a global lock to
synchronize the accesses, as otherwise we could deadlock GDBserver
(if the thread holding the lock stops for a signal, say). So
instead of that, we use a transaction scheme where GDBserver writes
always prevail over the IPAs writes, and, we have the IPA detect
the commit failure/overwrite, and retry the whole attempt. This is
mainly implemented by having a global token object that represents
who wrote last to the buffer control structure. We need to freeze
any inferior writing to the buffer while GDBserver touches memory,
so that the inferior can correctly detect that GDBserver had been
there, otherwise, it could mistakingly think its commit was
successful; that's implemented by simply having GDBserver set a
breakpoint the inferior hits if it is the critical region.
There are three cycling trace buffer control structure copies
(buffer head, tail, etc.), with the token object including an index
indicating which is current live copy. The IPA tentatively builds
an updated copy in a non-current control structure, while GDBserver
always clobbers the current version directly. The IPA then tries
to atomically "commit" its version; if GDBserver clobbered the
structure meanwhile, that will fail, and the IPA restarts the
allocation process.
Listing the step in further detail, we have:
In-process agent (producer):
- passes by `about_to_request_buffer_space' breakpoint/lock
- reads current token, extracts current trace buffer control index,
and starts tentatively updating the rightmost one (0->1, 1->2,
2->0). Note that only one inferior thread is executing this code
at any given time, due to an outer lock in the jump pads.
- updates counters, and tries to commit the token.
- passes by second `about_to_request_buffer_space' breakpoint/lock,
leaving the sync region.
- checks if the update was effective.
- if trace buffer was found full, hits flush_trace_buffer
breakpoint, and restarts later afterwards.
GDBserver (consumer):
- sets `about_to_request_buffer_space' breakpoint/lock.
- updates the token unconditionally, using the current buffer
control index, since it knows that the IP agent always writes to
the rightmost, and due to the breakpoint, at most one IP thread
can try to update the trace buffer concurrently to GDBserver, so
there will be no danger of trace buffer control index wrap making
the IPA write to the same index as GDBserver.
- flushes the IP agent's trace buffer completely, and updates the
current trace buffer control structure. GDBserver *always* wins.
- removes the `about_to_request_buffer_space' breakpoint.