X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Frecord-btrace.c;h=102e0ebe94ed1e68b5b1084d48efaf2319dfb5f9;hb=31fd9caad9fa8e13bbc132dce264f0c3bc53412f;hp=5ba4e0619b2ec772cef4ca11a4b5a62bc9e768a5;hpb=aef929023e3f9c68126564a96431935d35ce032e;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c index 5ba4e0619b..102e0ebe94 100644 --- a/gdb/record-btrace.c +++ b/gdb/record-btrace.c @@ -1,6 +1,6 @@ /* Branch trace support for GDB, the GNU debugger. - Copyright (C) 2013-2014 Free Software Foundation, Inc. + Copyright (C) 2013-2015 Free Software Foundation, Inc. Contributed by Intel Corp. @@ -26,7 +26,6 @@ #include "gdbcmd.h" #include "disasm.h" #include "observer.h" -#include "exceptions.h" #include "cli/cli-utils.h" #include "source.h" #include "ui-out.h" @@ -71,6 +70,20 @@ static struct async_event_handler *record_btrace_async_inferior_event_handler; /* A flag indicating that we are currently generating a core file. */ static int record_btrace_generating_corefile; +/* The current branch trace configuration. */ +static struct btrace_config record_btrace_conf; + +/* Command list for "record btrace". */ +static struct cmd_list_element *record_btrace_cmdlist; + +/* Command lists for "set/show record btrace". */ +static struct cmd_list_element *set_record_btrace_cmdlist; +static struct cmd_list_element *show_record_btrace_cmdlist; + +/* Command lists for "set/show record btrace bts". */ +static struct cmd_list_element *set_record_btrace_bts_cmdlist; +static struct cmd_list_element *show_record_btrace_bts_cmdlist; + /* Print a record-btrace debug message. Use do ... while (0) to avoid ambiguities when used in if statements. */ @@ -133,7 +146,7 @@ record_btrace_enable_warn (struct thread_info *tp) volatile struct gdb_exception error; TRY_CATCH (error, RETURN_MASK_ERROR) - btrace_enable (tp); + btrace_enable (tp, &record_btrace_conf); if (error.message != NULL) warning ("%s", error.message); @@ -188,7 +201,7 @@ record_btrace_handle_async_inferior_event (gdb_client_data data) /* The to_open method of target record-btrace. */ static void -record_btrace_open (char *args, int from_tty) +record_btrace_open (const char *args, int from_tty) { struct cleanup *disable_chain; struct thread_info *tp; @@ -200,9 +213,6 @@ record_btrace_open (char *args, int from_tty) if (!target_has_execution) error (_("The program is not being run.")); - if (!target_supports_btrace ()) - error (_("Target does not support branch tracing.")); - if (non_stop) error (_("Record btrace can't debug inferior in non-stop mode.")); @@ -212,7 +222,7 @@ record_btrace_open (char *args, int from_tty) ALL_NON_EXITED_THREADS (tp) if (args == NULL || *args == 0 || number_is_in_list (args, tp->num)) { - btrace_enable (tp); + btrace_enable (tp, &record_btrace_conf); make_cleanup (record_btrace_disable_callback, tp); } @@ -267,14 +277,96 @@ record_btrace_close (struct target_ops *self) btrace_teardown (tp); } +/* The to_async method of target record-btrace. */ + +static void +record_btrace_async (struct target_ops *ops, + void (*callback) (enum inferior_event_type event_type, + void *context), + void *context) +{ + if (callback != NULL) + mark_async_event_handler (record_btrace_async_inferior_event_handler); + else + clear_async_event_handler (record_btrace_async_inferior_event_handler); + + ops->beneath->to_async (ops->beneath, callback, context); +} + +/* Adjusts the size and returns a human readable size suffix. */ + +static const char * +record_btrace_adjust_size (unsigned int *size) +{ + unsigned int sz; + + sz = *size; + + if ((sz & ((1u << 30) - 1)) == 0) + { + *size = sz >> 30; + return "GB"; + } + else if ((sz & ((1u << 20) - 1)) == 0) + { + *size = sz >> 20; + return "MB"; + } + else if ((sz & ((1u << 10) - 1)) == 0) + { + *size = sz >> 10; + return "kB"; + } + else + return ""; +} + +/* Print a BTS configuration. */ + +static void +record_btrace_print_bts_conf (const struct btrace_config_bts *conf) +{ + const char *suffix; + unsigned int size; + + size = conf->size; + if (size > 0) + { + suffix = record_btrace_adjust_size (&size); + printf_unfiltered (_("Buffer size: %u%s.\n"), size, suffix); + } +} + +/* Print a branch tracing configuration. */ + +static void +record_btrace_print_conf (const struct btrace_config *conf) +{ + printf_unfiltered (_("Recording format: %s.\n"), + btrace_format_string (conf->format)); + + switch (conf->format) + { + case BTRACE_FORMAT_NONE: + return; + + case BTRACE_FORMAT_BTS: + record_btrace_print_bts_conf (&conf->bts); + return; + } + + internal_error (__FILE__, __LINE__, _("Unkown branch trace format.")); +} + /* The to_info_record method of target record-btrace. */ static void record_btrace_info (struct target_ops *self) { struct btrace_thread_info *btinfo; + const struct btrace_config *conf; struct thread_info *tp; - unsigned int insns, calls; + unsigned int insns, calls, gaps; DEBUG ("info"); @@ -282,12 +374,17 @@ record_btrace_info (struct target_ops *self) if (tp == NULL) error (_("No thread.")); + btinfo = &tp->btrace; + + conf = btrace_conf (btinfo); + if (conf != NULL) + record_btrace_print_conf (conf); + btrace_fetch (tp); insns = 0; calls = 0; - - btinfo = &tp->btrace; + gaps = 0; if (!btrace_is_empty (tp)) { @@ -299,19 +396,86 @@ record_btrace_info (struct target_ops *self) calls = btrace_call_number (&call); btrace_insn_end (&insn, btinfo); - btrace_insn_prev (&insn, 1); + insns = btrace_insn_number (&insn); + if (insns != 0) + { + /* The last instruction does not really belong to the trace. */ + insns -= 1; + } + else + { + unsigned int steps; + + /* Skip gaps at the end. */ + do + { + steps = btrace_insn_prev (&insn, 1); + if (steps == 0) + break; + + insns = btrace_insn_number (&insn); + } + while (insns == 0); + } + + gaps = btinfo->ngaps; } - printf_unfiltered (_("Recorded %u instructions in %u functions for thread " - "%d (%s).\n"), insns, calls, tp->num, - target_pid_to_str (tp->ptid)); + printf_unfiltered (_("Recorded %u instructions in %u functions (%u gaps) " + "for thread %d (%s).\n"), insns, calls, gaps, + tp->num, target_pid_to_str (tp->ptid)); if (btrace_is_replaying (tp)) printf_unfiltered (_("Replay in progress. At instruction %u.\n"), btrace_insn_number (btinfo->replay)); } +/* Print a decode error. */ + +static void +btrace_ui_out_decode_error (struct ui_out *uiout, int errcode, + enum btrace_format format) +{ + const char *errstr; + int is_error; + + errstr = _("unknown"); + is_error = 1; + + switch (format) + { + default: + break; + + case BTRACE_FORMAT_BTS: + switch (errcode) + { + default: + break; + + case BDE_BTS_OVERFLOW: + errstr = _("instruction overflow"); + break; + + case BDE_BTS_INSN_SIZE: + errstr = _("unknown instruction"); + break; + } + break; + } + + ui_out_text (uiout, _("[")); + if (is_error) + { + ui_out_text (uiout, _("decode error (")); + ui_out_field_int (uiout, "errcode", errcode); + ui_out_text (uiout, _("): ")); + } + ui_out_text (uiout, errstr); + ui_out_text (uiout, _("]\n")); +} + /* Print an unsigned int. */ static void @@ -324,6 +488,7 @@ ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val) static void btrace_insn_history (struct ui_out *uiout, + const struct btrace_thread_info *btinfo, const struct btrace_insn_iterator *begin, const struct btrace_insn_iterator *end, int flags) { @@ -341,13 +506,30 @@ btrace_insn_history (struct ui_out *uiout, insn = btrace_insn_get (&it); - /* Print the instruction index. */ - ui_out_field_uint (uiout, "index", btrace_insn_number (&it)); - ui_out_text (uiout, "\t"); + /* A NULL instruction indicates a gap in the trace. */ + if (insn == NULL) + { + const struct btrace_config *conf; + + conf = btrace_conf (btinfo); + + /* We have trace so we must have a configuration. */ + gdb_assert (conf != NULL); - /* Disassembly with '/m' flag may not produce the expected result. - See PR gdb/11833. */ - gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, insn->pc + 1); + btrace_ui_out_decode_error (uiout, it.function->errcode, + conf->format); + } + else + { + /* Print the instruction index. */ + ui_out_field_uint (uiout, "index", btrace_insn_number (&it)); + ui_out_text (uiout, "\t"); + + /* Disassembly with '/m' flag may not produce the expected result. + See PR gdb/11833. */ + gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, + insn->pc + 1); + } } } @@ -424,7 +606,7 @@ record_btrace_insn_history (struct target_ops *self, int size, int flags) } if (covered > 0) - btrace_insn_history (uiout, &begin, &end, flags); + btrace_insn_history (uiout, btinfo, &begin, &end, flags); else { if (size < 0) @@ -484,7 +666,7 @@ record_btrace_insn_history_range (struct target_ops *self, btrace_insn_next (&end, 1); } - btrace_insn_history (uiout, &begin, &end, flags); + btrace_insn_history (uiout, btinfo, &begin, &end, flags); btrace_set_insn_history (btinfo, &begin, &end); do_cleanups (uiout_cleanup); @@ -557,7 +739,7 @@ btrace_call_history_src_line (struct ui_out *uiout, return; ui_out_field_string (uiout, "file", - symtab_to_filename_for_display (sym->symtab)); + symtab_to_filename_for_display (symbol_symtab (sym))); begin = bfun->lbegin; end = bfun->lend; @@ -625,6 +807,21 @@ btrace_call_history (struct ui_out *uiout, ui_out_field_uint (uiout, "index", bfun->number); ui_out_text (uiout, "\t"); + /* Indicate gaps in the trace. */ + if (bfun->errcode != 0) + { + const struct btrace_config *conf; + + conf = btrace_conf (btinfo); + + /* We have trace so we must have a configuration. */ + gdb_assert (conf != NULL); + + btrace_ui_out_decode_error (uiout, bfun->errcode, conf->format); + + continue; + } + if ((flags & RECORD_PRINT_INDENT_CALLS) != 0) { int level = bfun->level + btinfo->level, i; @@ -896,13 +1093,9 @@ record_btrace_xfer_partial (struct target_ops *ops, enum target_object object, } /* Forward the request. */ - for (ops = ops->beneath; ops != NULL; ops = ops->beneath) - if (ops->to_xfer_partial != NULL) - return ops->to_xfer_partial (ops, object, annex, readbuf, writebuf, - offset, len, xfered_len); - - *xfered_len = len; - return TARGET_XFER_UNAVAILABLE; + ops = ops->beneath; + return ops->to_xfer_partial (ops, object, annex, readbuf, writebuf, + offset, len, xfered_len); } /* The to_insert_breakpoint method of target record-btrace. */ @@ -996,14 +1189,9 @@ record_btrace_fetch_registers (struct target_ops *ops, } else { - struct target_ops *t; + struct target_ops *t = ops->beneath; - for (t = ops->beneath; t != NULL; t = t->beneath) - if (t->to_fetch_registers != NULL) - { - t->to_fetch_registers (t, regcache, regno); - break; - } + t->to_fetch_registers (t, regcache, regno); } } @@ -1020,14 +1208,8 @@ record_btrace_store_registers (struct target_ops *ops, gdb_assert (may_write_registers != 0); - for (t = ops->beneath; t != NULL; t = t->beneath) - if (t->to_store_registers != NULL) - { - t->to_store_registers (t, regcache, regno); - return; - } - - noprocess (); + t = ops->beneath; + t->to_store_registers (t, regcache, regno); } /* The to_prepare_to_store method of target record-btrace. */ @@ -1041,12 +1223,8 @@ record_btrace_prepare_to_store (struct target_ops *ops, if (!record_btrace_generating_corefile && record_btrace_is_replaying (ops)) return; - for (t = ops->beneath; t != NULL; t = t->beneath) - if (t->to_prepare_to_store != NULL) - { - t->to_prepare_to_store (t, regcache); - return; - } + t = ops->beneath; + t->to_prepare_to_store (t, regcache); } /* The branch trace frame cache. */ @@ -1457,6 +1635,16 @@ record_btrace_start_replaying (struct thread_info *tp) replay = xmalloc (sizeof (*replay)); btrace_insn_end (replay, btinfo); + /* Skip gaps at the end of the trace. */ + while (btrace_insn_get (replay) == NULL) + { + unsigned int steps; + + steps = btrace_insn_prev (replay, 1); + if (steps == 0) + error (_("No trace.")); + } + /* We're not replaying, yet. */ gdb_assert (btinfo->replay == NULL); btinfo->replay = replay; @@ -1533,11 +1721,8 @@ record_btrace_resume (struct target_ops *ops, ptid_t ptid, int step, /* As long as we're not replaying, just forward the request. */ if (!record_btrace_is_replaying (ops) && execution_direction != EXEC_REVERSE) { - for (ops = ops->beneath; ops != NULL; ops = ops->beneath) - if (ops->to_resume != NULL) - return ops->to_resume (ops, ptid, step, signal); - - error (_("Cannot find target for stepping.")); + ops = ops->beneath; + return ops->to_resume (ops, ptid, step, signal); } /* Compute the btrace thread flag for the requested move. */ @@ -1655,9 +1840,17 @@ record_btrace_step_thread (struct thread_info *tp) if (replay == NULL) return btrace_step_no_history (); - /* We are always able to step at least once. */ - steps = btrace_insn_next (replay, 1); - gdb_assert (steps == 1); + /* Skip gaps during replay. */ + do + { + steps = btrace_insn_next (replay, 1); + if (steps == 0) + { + record_btrace_stop_replaying (tp); + return btrace_step_no_history (); + } + } + while (btrace_insn_get (replay) == NULL); /* Determine the end of the instruction trace. */ btrace_insn_end (&end, btinfo); @@ -1673,10 +1866,16 @@ record_btrace_step_thread (struct thread_info *tp) if (replay == NULL) replay = record_btrace_start_replaying (tp); - /* If we can't step any further, we reached the end of the history. */ - steps = btrace_insn_prev (replay, 1); - if (steps == 0) - return btrace_step_no_history (); + /* If we can't step any further, we reached the end of the history. + Skip gaps during replay. */ + do + { + steps = btrace_insn_prev (replay, 1); + if (steps == 0) + return btrace_step_no_history (); + + } + while (btrace_insn_get (replay) == NULL); return btrace_step_stopped (); @@ -1685,7 +1884,7 @@ record_btrace_step_thread (struct thread_info *tp) if (replay == NULL) return btrace_step_no_history (); - inf = find_inferior_pid (ptid_get_pid (tp->ptid)); + inf = find_inferior_ptid (tp->ptid); aspace = inf->aspace; /* Determine the end of the instruction trace. */ @@ -1695,9 +1894,19 @@ record_btrace_step_thread (struct thread_info *tp) { const struct btrace_insn *insn; - /* We are always able to step at least once. */ - steps = btrace_insn_next (replay, 1); - gdb_assert (steps == 1); + /* Skip gaps during replay. */ + do + { + steps = btrace_insn_next (replay, 1); + if (steps == 0) + { + record_btrace_stop_replaying (tp); + return btrace_step_no_history (); + } + + insn = btrace_insn_get (replay); + } + while (insn == NULL); /* We stop replaying if we reached the end of the trace. */ if (btrace_insn_cmp (replay, &end) == 0) @@ -1706,9 +1915,6 @@ record_btrace_step_thread (struct thread_info *tp) return btrace_step_no_history (); } - insn = btrace_insn_get (replay); - gdb_assert (insn); - DEBUG ("stepping %d (%s) ... %s", tp->num, target_pid_to_str (tp->ptid), core_addr_to_string_nz (insn->pc)); @@ -1722,20 +1928,24 @@ record_btrace_step_thread (struct thread_info *tp) if (replay == NULL) replay = record_btrace_start_replaying (tp); - inf = find_inferior_pid (ptid_get_pid (tp->ptid)); + inf = find_inferior_ptid (tp->ptid); aspace = inf->aspace; for (;;) { const struct btrace_insn *insn; - /* If we can't step any further, we're done. */ - steps = btrace_insn_prev (replay, 1); - if (steps == 0) - return btrace_step_no_history (); + /* If we can't step any further, we reached the end of the history. + Skip gaps during replay. */ + do + { + steps = btrace_insn_prev (replay, 1); + if (steps == 0) + return btrace_step_no_history (); - insn = btrace_insn_get (replay); - gdb_assert (insn); + insn = btrace_insn_get (replay); + } + while (insn == NULL); DEBUG ("reverse-stepping %d (%s) ... %s", tp->num, target_pid_to_str (tp->ptid), @@ -1760,11 +1970,8 @@ record_btrace_wait (struct target_ops *ops, ptid_t ptid, /* As long as we're not replaying, just forward the request. */ if (!record_btrace_is_replaying (ops) && execution_direction != EXEC_REVERSE) { - for (ops = ops->beneath; ops != NULL; ops = ops->beneath) - if (ops->to_wait != NULL) - return ops->to_wait (ops, ptid, status, options); - - error (_("Cannot find target for waiting.")); + ops = ops->beneath; + return ops->to_wait (ops, ptid, status, options); } /* Let's find a thread to move. */ @@ -1816,22 +2023,18 @@ record_btrace_decr_pc_after_break (struct target_ops *ops, return ops->beneath->to_decr_pc_after_break (ops->beneath, gdbarch); } -/* The to_find_new_threads method of target record-btrace. */ +/* The to_update_thread_list method of target record-btrace. */ static void -record_btrace_find_new_threads (struct target_ops *ops) +record_btrace_update_thread_list (struct target_ops *ops) { - /* Don't expect new threads if we're replaying. */ + /* We don't add or remove threads during replay. */ if (record_btrace_is_replaying (ops)) return; /* Forward the request. */ - for (ops = ops->beneath; ops != NULL; ops = ops->beneath) - if (ops->to_find_new_threads != NULL) - { - ops->to_find_new_threads (ops); - break; - } + ops = ops->beneath; + ops->to_update_thread_list (ops); } /* The to_thread_alive method of target record-btrace. */ @@ -1844,11 +2047,8 @@ record_btrace_thread_alive (struct target_ops *ops, ptid_t ptid) return find_thread_ptid (ptid) != NULL; /* Forward the request. */ - for (ops = ops->beneath; ops != NULL; ops = ops->beneath) - if (ops->to_thread_alive != NULL) - return ops->to_thread_alive (ops, ptid); - - return 0; + ops = ops->beneath; + return ops->to_thread_alive (ops, ptid); } /* Set the replay branch trace instruction iterator. If IT is NULL, replay @@ -1973,6 +2173,7 @@ init_record_btrace_ops (void) ops->to_doc = "Collect control-flow trace and provide the execution history."; ops->to_open = record_btrace_open; ops->to_close = record_btrace_close; + ops->to_async = record_btrace_async; ops->to_detach = record_detach; ops->to_disconnect = record_disconnect; ops->to_mourn_inferior = record_mourn_inferior; @@ -1996,7 +2197,7 @@ init_record_btrace_ops (void) ops->to_get_tailcall_unwinder = &record_btrace_to_get_tailcall_unwinder; ops->to_resume = record_btrace_resume; ops->to_wait = record_btrace_wait; - ops->to_find_new_threads = record_btrace_find_new_threads; + ops->to_update_thread_list = record_btrace_update_thread_list; ops->to_thread_alive = record_btrace_thread_alive; ops->to_goto_record_begin = record_btrace_goto_begin; ops->to_goto_record_end = record_btrace_goto_end; @@ -2010,15 +2211,48 @@ init_record_btrace_ops (void) ops->to_magic = OPS_MAGIC; } +/* Start recording in BTS format. */ + +static void +cmd_record_btrace_bts_start (char *args, int from_tty) +{ + volatile struct gdb_exception exception; + + if (args != NULL && *args != 0) + error (_("Invalid argument.")); + + record_btrace_conf.format = BTRACE_FORMAT_BTS; + + TRY_CATCH (exception, RETURN_MASK_ALL) + execute_command ("target record-btrace", from_tty); + + if (exception.error != 0) + { + record_btrace_conf.format = BTRACE_FORMAT_NONE; + throw_exception (exception); + } +} + /* Alias for "target record". */ static void cmd_record_btrace_start (char *args, int from_tty) { + volatile struct gdb_exception exception; + if (args != NULL && *args != 0) error (_("Invalid argument.")); - execute_command ("target record-btrace", from_tty); + record_btrace_conf.format = BTRACE_FORMAT_BTS; + + TRY_CATCH (exception, RETURN_MASK_ALL) + execute_command ("target record-btrace", from_tty); + + if (exception.error == 0) + return; + + record_btrace_conf.format = BTRACE_FORMAT_NONE; + throw_exception (exception); } /* The "set record btrace" command. */ @@ -2047,6 +2281,25 @@ cmd_show_replay_memory_access (struct ui_file *file, int from_tty, replay_memory_access); } +/* The "set record btrace bts" command. */ + +static void +cmd_set_record_btrace_bts (char *args, int from_tty) +{ + printf_unfiltered (_("\"set record btrace bts\" must be followed " + "by an apporpriate subcommand.\n")); + help_list (set_record_btrace_bts_cmdlist, "set record btrace bts ", + all_commands, gdb_stdout); +} + +/* The "show record btrace bts" command. */ + +static void +cmd_show_record_btrace_bts (char *args, int from_tty) +{ + cmd_show_list (show_record_btrace_bts_cmdlist, from_tty, ""); +} + void _initialize_record_btrace (void); /* Initialize btrace commands. */ @@ -2054,11 +2307,19 @@ void _initialize_record_btrace (void); void _initialize_record_btrace (void) { - add_cmd ("btrace", class_obscure, cmd_record_btrace_start, - _("Start branch trace recording."), - &record_cmdlist); + add_prefix_cmd ("btrace", class_obscure, cmd_record_btrace_start, + _("Start branch trace recording."), &record_btrace_cmdlist, + "record btrace ", 0, &record_cmdlist); add_alias_cmd ("b", "btrace", class_obscure, 1, &record_cmdlist); + add_cmd ("bts", class_obscure, cmd_record_btrace_bts_start, + _("\ +Start branch trace recording in Branch Trace Store (BTS) format.\n\n\ +The processor stores a from/to record for each branch into a cyclic buffer.\n\ +This format may not be available on all processors."), + &record_btrace_cmdlist); + add_alias_cmd ("bts", "btrace bts", class_obscure, 1, &record_cmdlist); + add_prefix_cmd ("btrace", class_support, cmd_set_record_btrace, _("Set record options"), &set_record_btrace_cmdlist, "set record btrace ", 0, &set_record_cmdlist); @@ -2082,9 +2343,34 @@ replay."), &set_record_btrace_cmdlist, &show_record_btrace_cmdlist); + add_prefix_cmd ("bts", class_support, cmd_set_record_btrace_bts, + _("Set record btrace bts options"), + &set_record_btrace_bts_cmdlist, + "set record btrace bts ", 0, &set_record_btrace_cmdlist); + + add_prefix_cmd ("bts", class_support, cmd_show_record_btrace_bts, + _("Show record btrace bts options"), + &show_record_btrace_bts_cmdlist, + "show record btrace bts ", 0, &show_record_btrace_cmdlist); + + add_setshow_uinteger_cmd ("buffer-size", no_class, + &record_btrace_conf.bts.size, + _("Set the record/replay bts buffer size."), + _("Show the record/replay bts buffer size."), _("\ +When starting recording request a trace buffer of this size. \ +The actual buffer size may differ from the requested size. \ +Use \"info record\" to see the actual buffer size.\n\n\ +Bigger buffers allow longer recording but also take more time to process \ +the recorded execution trace.\n\n\ +The trace buffer size may not be changed while recording."), NULL, NULL, + &set_record_btrace_bts_cmdlist, + &show_record_btrace_bts_cmdlist); + init_record_btrace_ops (); add_target (&record_btrace_ops); bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL, xcalloc, xfree); + + record_btrace_conf.bts.size = 64 * 1024; }