--- a/gdbserver/config.in +++ b/gdbserver/config.in @@ -303,3 +303,6 @@ /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE + +/* Define to 1 if you want target attributes break count. */ +#undef BREAK_COUNT --- a/gdbserver/configure +++ b/gdbserver/configure @@ -693,6 +693,7 @@ with_pkgversion with_bugurl with_libthread_db enable_inprocess_agent +enable_break_count ' ac_precious_vars='build_alias host_alias @@ -1326,6 +1327,7 @@ Optional Features: --enable-werror treat compile warnings as errors --enable-inprocess-agent inprocess agent + --enable-break-count enable target attribute break count Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -5705,6 +5707,18 @@ if $want_ipa ; then fi fi +# Check whether --enable_break_count was given. +if test "${enable_break_count+set}" = set; then : + want_bc=true +else + want_bc=false +fi + + +if $want_bc ; then + $as_echo "#define BREAK_COUNT 1" >>confdefs.h +fi + --- a/gdbserver/configure.ac +++ b/gdbserver/configure.ac @@ -451,6 +451,16 @@ if $want_ipa ; then fi fi +AC_ARG_ENABLE(break-count, +AS_HELP_STRING([--enable-break-count], [enable target attribute break count]), +[want_bc=true]) ;; +esac], +[want_bc=false]) + +if $want_bc ; then + $as_echo "#define BREAK_COUNT 1" >>confdefs.h +fi + AC_SUBST(GDBSERVER_DEPFILES) AC_SUBST(GDBSERVER_LIBS) AC_SUBST(USE_THREAD_DB) --- a/gdbserver/inferiors.c +++ b/gdbserver/inferiors.c @@ -265,6 +265,9 @@ remove_process (struct process_info *pro clear_symbol_cache (&process->symbol_cache); free_all_breakpoints (process); remove_inferior (&all_processes, &process->head); +#ifdef BREAK_COUNT + break_count_list_remove_all (process); +#endif free (process); } --- a/gdbserver/linux-low.c +++ b/gdbserver/linux-low.c @@ -2613,6 +2613,10 @@ Check if we're already there.\n", goto retry; } +#ifdef BREAK_COUNT + break_count_increase (event_child->stop_pc); +#endif + /* If GDB wanted this thread to single step, we always want to report the SIGTRAP, and let GDB handle it. Watchpoints should always be reported. So should signals we can't explain. A --- a/gdbserver/mem-break.c +++ b/gdbserver/mem-break.c @@ -48,6 +48,15 @@ int breakpoint_len; them will point to the same raw breakpoint, which is reference counted. */ +#ifdef BREAK_COUNT +struct break_count_s +{ + struct break_count_s *next; + CORE_ADDR addr; + uint64_t count; +}; +#endif + /* The low level, physical, raw breakpoint. */ struct raw_breakpoint { @@ -71,6 +80,11 @@ struct raw_breakpoint /* Non-zero if this breakpoint is currently disabled because we no longer detect it as inserted. */ int shlib_disabled; + +#ifdef BREAK_COUNT + /* Point to the break_count of this address. */ + struct break_count_s *count; +#endif }; /* The type of a breakpoint. */ @@ -138,6 +152,50 @@ struct breakpoint int (*handler) (CORE_ADDR); }; +#ifdef BREAK_COUNT +static struct break_count_s * +break_count_list_create (struct process_info *proc, CORE_ADDR addr) +{ + struct break_count_s *bc; + + bc = xcalloc (1, sizeof (struct break_count_s)); + bc->addr = addr; + bc->count = 0; + bc->next = proc->break_count_list; + proc->break_count_list = bc; + + return bc; +} + +static int +break_count_list_remove(struct process_info *proc, CORE_ADDR addr) +{ + struct break_count_s *bc = NULL, *prev = NULL; + + for (bc = proc->break_count_list; bc; bc = bc->next) + { + if (bc->addr == addr) + break; + prev = bc; + } + + if (!bc) + return -1; + + if (prev) + prev->next = bc->next; + else + proc->break_count_list = bc->next; + + if (bc == proc->break_count_select) + proc->break_count_select = NULL; + + xfree(bc); + + return 0; +} +#endif + int any_persistent_commands () { @@ -832,6 +890,10 @@ add_breakpoint_condition (CORE_ADDR addr /* Evaluate condition (if any) at breakpoint BP. Return 1 if true and 0 otherwise. */ +#ifdef BREAK_COUNT +static struct break_count_s *current_break_count = NULL; +#endif + int gdb_condition_true_at_breakpoint (CORE_ADDR where) { @@ -859,12 +921,18 @@ gdb_condition_true_at_breakpoint (CORE_A If we failed to evaluate the expression, TRUE is returned. This forces GDB to reevaluate the conditions. */ +#ifdef BREAK_COUNT + current_break_count = bp->raw->count; +#endif for (cl = bp->cond_list; cl && !value && !err; cl = cl->next) { /* Evaluate the condition. */ err = gdb_eval_agent_expr (&ctx, cl->cond, &value); } +#ifdef BREAK_COUNT + current_break_count = NULL; +#endif if (err) return 1; @@ -1443,3 +1511,221 @@ free_all_breakpoints (struct process_inf while (proc->breakpoints) delete_breakpoint_1 (proc, proc->breakpoints); } + +#ifdef BREAK_COUNT +void +break_count_list_remove_all(struct process_info *proc) +{ + struct raw_breakpoint *bp; + struct break_count_s *bc, *tmp; + + for (bp = proc->raw_breakpoints; bp; bp = bp->next) + bp->count = NULL; + + for (bc = proc->break_count_list; bc ? (tmp=bc->next, 1): 0; + bc = tmp) + xfree(bc); + + proc->break_count_list = NULL; + proc->break_count_select = NULL; +} + +struct break_count_list_reset_s +{ + struct thread_resume *resume_info; + size_t n; +}; + +static int +break_count_list_reset_1 (struct inferior_list_entry *inf, void *p) +{ + struct process_info *proc = (struct process_info *)inf; + struct break_count_s *bc, *tmp; + int i; + struct break_count_list_reset_s *arg = p; + + if (proc->break_count_on == 0) + return 0; + + for (i = 0; i < arg->n; i++) + { + if ((ptid_equal (minus_one_ptid, arg->resume_info[i].thread) + || ptid_get_pid (inf->id) + == ptid_get_pid (arg->resume_info[i].thread)) + && arg->resume_info[i].kind == resume_continue) + break; + } + if (i >= arg->n) + return 0; + + for (bc = proc->break_count_list; bc ? (tmp=bc->next, 1): 0; bc = tmp) + { + struct raw_breakpoint *bp; + + for (bp = proc->raw_breakpoints; bp; bp = bp->next) + { + if (bp->pc == bc->addr) + break; + } + if (!bp) + { + break_count_list_remove(proc, bc->addr); + continue; + } + + bp->count = bc; + if (proc->break_count_on == 1) + bc->count = 0; + } + + return 0; +} + +void +break_count_list_reset (struct thread_resume *resume_info, size_t n) +{ + struct break_count_list_reset_s arg; + + arg.resume_info = resume_info; + arg.n = n; + + find_inferior (&all_processes, break_count_list_reset_1, &arg); +} + +int +break_count_val (int id, LONGEST *val) +{ + if (id == 4 && current_break_count) + { + *val = current_break_count->count; + return 1; + } + + return 0; +} + +void +break_count_increase (CORE_ADDR addr) +{ + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; + + if (proc->break_count_on == 0) + return; + + bp = find_raw_breakpoint_at (addr); + if (bp == NULL) + return; + + if (!bp->count) + bp->count = break_count_list_create (proc, addr); + + bp->count->count++; +} + +int +get_target_attribute (int id, ULONGEST *val) +{ + struct process_info *proc = current_process (); + + if (proc->break_count_on == 0 && id != 2) + return -1; + + switch (id) + { + case 2: + if (proc->break_count_on == 0) + *val = 0; + else if (proc->break_count_on == 1) + *val = 1; + else + *val = 2; + break; + case 3: + if (!proc->break_count_select) + proc->break_count_select = proc->break_count_list; + if (proc->break_count_select) + *val = (ULONGEST)(uint64_t)proc->break_count_select->addr; + else + return -1; + break; + case 4: + if (!proc->break_count_select) + proc->break_count_select = proc->break_count_list; + if (proc->break_count_select) + *val = (ULONGEST)proc->break_count_select->count; + else + return -1; + break; + default: + return -1; + break; + } + + return 0; +} + +int +set_target_attribute (int id, ULONGEST val) +{ + struct process_info *proc = current_process (); + + if (proc->break_count_on == 0 && id != 2) + return -1; + + switch (id) + { + case 2: + if (val == 0) + { + proc->break_count_on = 0; + break_count_list_remove_all (proc); + } + else + { + if (val == 1) + proc->break_count_on = 1; + else + proc->break_count_on = 2; + } + break; + case 3: + { + struct break_count_s *bc = NULL; + + for (bc = proc->break_count_list; bc; bc = bc->next) + { + if (bc->addr == (CORE_ADDR) val) + break; + } + if (bc) + proc->break_count_select = bc; + else + { + struct raw_breakpoint *bp; + + bp = find_raw_breakpoint_at ((CORE_ADDR) val); + if (bp) + { + bp->count = break_count_list_create (proc, (CORE_ADDR) val); + proc->break_count_select = bp->count; + } + else + return -1; + } + } + break; + case 4: + if (proc->break_count_select) + proc->break_count_select->count = (uint64_t) val; + else + return -1; + break; + default: + return -1; + break; + } + + return 0; +} +#endif --- a/gdbserver/mem-break.h +++ b/gdbserver/mem-break.h @@ -184,4 +184,19 @@ void uninsert_fast_tracepoint_jumps_at ( void reinsert_fast_tracepoint_jumps_at (CORE_ADDR where); +#ifdef BREAK_COUNT +void break_count_list_remove_all(struct process_info *proc); + +void break_count_list_reset (struct thread_resume *resume_info, + size_t n); + +int break_count_val (int id, LONGEST *val); + +void break_count_increase (CORE_ADDR addr); + +int get_target_attribute (int id, ULONGEST *val); + +int set_target_attribute (int id, ULONGEST val); +#endif + #endif /* MEM_BREAK_H */ --- a/gdbserver/server.c +++ b/gdbserver/server.c @@ -265,6 +265,9 @@ start_inferior (char **argv) do { +#ifdef BREAK_COUNT + break_count_list_reset (&resume_info, 1); +#endif (*the_target->resume) (&resume_info, 1); last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); @@ -552,6 +555,27 @@ handle_general_set (char *own_buf) return; } +#ifdef BREAK_COUNT + if (strncmp ("QTA:", own_buf, 4) == 0) + { + ULONGEST id; + ULONGEST val; + char *packet = own_buf; + + packet += 4; + + packet = unpack_varlen_hex (packet, &id); + ++packet; /* skip a colon */ + packet = unpack_varlen_hex (packet, &val); + + if (set_target_attribute ((int) id, val)) + strcpy (own_buf, "E00"); + else + strcpy (own_buf, "OK"); + return; + } +#endif + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -1251,6 +1275,59 @@ handle_qxfer_fdpic (const char *annex, g return (*the_target->read_loadmap) (annex, offset, readbuf, len); } +#ifdef BREAK_COUNT +/* Handle qXfer:target-attributes:read. */ + +static int +handle_qxfer_target_attributes (const char *annex, + gdb_byte *readbuf, + const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + /* The id 1 is for $trace_timestamp. */ + static const char *document = " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +"; + size_t total_len; + + if (writebuf != NULL) + return -2; + + if (!target_running ()) + return -1; + + total_len = strlen (document); + + if (offset > total_len) + return -1; + + if (offset + len > total_len) + len = total_len - offset; + + memcpy (readbuf, document + offset, len); + return len; +} +#endif + static const struct qxfer qxfer_packets[] = { { "auxv", handle_qxfer_auxv }, @@ -1264,6 +1341,9 @@ static const struct qxfer qxfer_packets[ { "statictrace", handle_qxfer_statictrace }, { "threads", handle_qxfer_threads }, { "traceframe-info", handle_qxfer_traceframe_info }, +#ifdef BREAK_COUNT + { "target-attributes", handle_qxfer_target_attributes }, +#endif }; static int @@ -1647,6 +1727,10 @@ handle_query (char *own_buf, int packet_ if (target_supports_agent ()) strcat (own_buf, ";QAgent+"); +#ifdef BREAK_COUNT + strcat (own_buf, ";qXfer:target-attributes:read+"); +#endif + return; } @@ -1831,6 +1915,21 @@ handle_query (char *own_buf, int packet_ return; } +#ifdef BREAK_COUNT + if (strncmp ("qTA:", own_buf, 4) == 0) + { + ULONGEST id; + ULONGEST val; + + unpack_varlen_hex (own_buf + 4, &id); + if (get_target_attribute ((int)id, &val)) + strcpy (own_buf, "U"); + else + sprintf (own_buf, "V%s", phex_nz (val, 0)); + return; + } +#endif + if (handle_qxfer (own_buf, packet_len, new_packet_len_p)) return; @@ -1942,6 +2041,9 @@ handle_v_cont (char *own_buf) if (!non_stop) enable_async_io (); +#ifdef BREAK_COUNT + break_count_list_reset (resume_info, n); +#endif (*the_target->resume) (resume_info, n); free (resume_info); @@ -2231,6 +2333,9 @@ myresume (char *own_buf, int step, int s if (!non_stop) enable_async_io (); +#ifdef BREAK_COUNT + break_count_list_reset (resume_info, n); +#endif (*the_target->resume) (resume_info, n); if (non_stop) @@ -2984,6 +3089,9 @@ process_serial_event (void) resume_info.thread = minus_one_ptid; resume_info.kind = resume_continue; resume_info.sig = 0; +#ifdef BREAK_COUNT + break_count_list_reset (&resume_info, 1); +#endif (*the_target->resume) (&resume_info, 1); write_ok (own_buf); @@ -3427,6 +3535,9 @@ handle_target_event (int err, gdb_client resume_info.thread = last_ptid; resume_info.kind = resume_continue; resume_info.sig = gdb_signal_to_host (last_status.value.sig); +#ifdef BREAK_COUNT + break_count_list_reset (&resume_info, 1); +#endif (*the_target->resume) (&resume_info, 1); } else if (debug_threads) --- a/gdbserver/server.h +++ b/gdbserver/server.h @@ -132,6 +132,9 @@ struct breakpoint; struct raw_breakpoint; struct fast_tracepoint_jump; struct process_info_private; +#ifdef BREAK_COUNT +struct break_count_s; +#endif struct process_info { @@ -157,6 +160,14 @@ struct process_info /* The list of installed fast tracepoints. */ struct fast_tracepoint_jump *fast_tracepoint_jumps; +#ifdef BREAK_COUNT + int break_count_on; + + struct break_count_s *break_count_list; + + struct break_count_s *break_count_select; +#endif + /* Private target data. */ struct process_info_private *private; }; --- a/gdbserver/tracepoint.c +++ b/gdbserver/tracepoint.c @@ -2096,6 +2096,15 @@ get_trace_state_variable_value (int num) { struct trace_state_variable *tsv; +#ifdef BREAK_COUNT +#ifndef IN_PROCESS_AGENT + LONGEST ret; + + if (break_count_val (num, &ret)) + return ret; +#endif +#endif + tsv = get_trace_state_variable (num); if (!tsv)