0743fc83c0
Currently there are many prefix commands that do nothing but call either help_list or cmd_show_list. I happened to notice that one such call, for "set print type", used the wrong command list parameter, causing incorrect output. Rather than fix this bug in isolation, I decided to eliminate this possibility by adding two new ways to add prefix commands, which simply route the call to help_list or cmd_show_list, as appropriate. This makes it impossible for a mismatch to occur. In some cases, a bit of output was removed; however, I don't think this output in general was very useful. It seemed redundant with what's already printed by help_list. A representative example is this hunk, removed from ada-lang.c: - printf_unfiltered (_(\ -"\"set ada\" must be followed by the name of a setting.\n")); This simplified the CLI style set/show commands quite a bit, and allowed the deletion of a macro. This also cleans up some unusual code in windows-tdep.c. Tested on x86-64 Fedora 30. Note that I have no way to build the go32-nat.c change. gdb/ChangeLog 2020-04-17 Tom Tromey <tromey@adacore.com> * auto-load.c (show_auto_load_cmd): Remove. (auto_load_show_cmdlist_get): Use add_show_prefix_cmd. * arc-tdep.c (_initialize_arc_tdep): Use add_show_prefix_cmd. (maintenance_print_arc_command): Remove. * tui/tui-win.c (tui_command): Remove. (tui_get_cmd_list): Use add_basic_prefix_cmd. * tui/tui-layout.c (tui_layout_command): Remove. (_initialize_tui_layout): Use add_basic_prefix_cmd. * python/python.c (user_set_python, user_show_python): Remove. (_initialize_python): Use add_basic_prefix_cmd, add_show_prefix_cmd. * guile/guile.c (set_guile_command, show_guile_command): Remove. (install_gdb_commands): Use add_basic_prefix_cmd, add_show_prefix_cmd. (info_guile_command): Remove. * dwarf2/read.c (set_dwarf_cmd, show_dwarf_cmd): Remove. (_initialize_dwarf2_read): Use add_basic_prefix_cmd, add_show_prefix_cmd. * cli/cli-style.h (class cli_style_option) <add_setshow_commands>: Remove do_set and do_show parameters. * cli/cli-style.c (set_style, show_style): Remove. (_initialize_cli_style): Use add_basic_prefix_cmd, add_show_prefix_cmd. (cli_style_option::add_setshow_commands): Remove do_set and do_show parameters. (cli_style_option::add_setshow_commands): Use add_basic_prefix_cmd, add_show_prefix_cmd. (STYLE_ADD_SETSHOW_COMMANDS): Remove macro. (set_style_name): Remove. * cli/cli-dump.c (dump_command, append_command): Remove. (srec_dump_command, ihex_dump_command, verilog_dump_command) (tekhex_dump_command, binary_dump_command) (binary_append_command): Remove. (_initialize_cli_dump): Use add_basic_prefix_cmd. * windows-tdep.c (w32_prefix_command_valid): Remove global. (init_w32_command_list): Remove; move into ... (_initialize_windows_tdep): ... here. Use add_basic_prefix_cmd. * valprint.c (set_print, show_print, set_print_raw) (show_print_raw): Remove. (_initialize_valprint): Use add_basic_prefix_cmd, add_show_prefix_cmd. * typeprint.c (set_print_type, show_print_type): Remove. (_initialize_typeprint): Use add_basic_prefix_cmd, add_show_prefix_cmd. * record.c (set_record_command, show_record_command): Remove. (_initialize_record): Use add_basic_prefix_cmd, add_show_prefix_cmd. * cli/cli-cmds.c (_initialize_cli_cmds): Use add_basic_prefix_cmd, add_show_prefix_cmd. (info_command, show_command, set_debug, show_debug): Remove. * top.h (set_history, show_history): Don't declare. * top.c (set_history, show_history): Remove. * target-descriptions.c (set_tdesc_cmd, show_tdesc_cmd) (unset_tdesc_cmd): Remove. (_initialize_target_descriptions): Use add_basic_prefix_cmd, add_show_prefix_cmd. * symtab.c (info_module_command): Remove. (_initialize_symtab): Use add_basic_prefix_cmd. * symfile.c (overlay_command): Remove. (_initialize_symfile): Use add_basic_prefix_cmd. * sparc64-tdep.c (info_adi_command): Remove. (_initialize_sparc64_adi_tdep): Use add_basic_prefix_cmd. * sh-tdep.c (show_sh_command, set_sh_command): Remove. (_initialize_sh_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * serial.c (serial_set_cmd, serial_show_cmd): Remove. (_initialize_serial): Use add_basic_prefix_cmd, add_show_prefix_cmd. * ser-tcp.c (set_tcp_cmd, show_tcp_cmd): Remove. (_initialize_ser_tcp): Use add_basic_prefix_cmd, add_show_prefix_cmd. * rs6000-tdep.c (set_powerpc_command, show_powerpc_command) (_initialize_rs6000_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * riscv-tdep.c (show_riscv_command, set_riscv_command) (show_debug_riscv_command, set_debug_riscv_command): Remove. (_initialize_riscv_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * remote.c (remote_command, set_remote_cmd): Remove. (_initialize_remote): Use add_basic_prefix_cmd. * record-full.c (set_record_full_command) (show_record_full_command): Remove. (_initialize_record_full): Use add_basic_prefix_cmd, add_show_prefix_cmd. * record-btrace.c (cmd_set_record_btrace) (cmd_show_record_btrace, cmd_set_record_btrace_bts) (cmd_show_record_btrace_bts, cmd_set_record_btrace_pt) (cmd_show_record_btrace_pt): Remove. (_initialize_record_btrace): Use add_basic_prefix_cmd, add_show_prefix_cmd. * ravenscar-thread.c (set_ravenscar_command) (show_ravenscar_command): Remove. (_initialize_ravenscar): Use add_basic_prefix_cmd, add_show_prefix_cmd. * mips-tdep.c (show_mips_command, set_mips_command) (_initialize_mips_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * maint.c (maintenance_command, maintenance_info_command) (maintenance_check_command, maintenance_print_command) (maintenance_set_cmd, maintenance_show_cmd): Remove. (_initialize_maint_cmds): Use add_basic_prefix_cmd, add_show_prefix_cmd. (show_per_command_cmd): Remove. * maint-test-settings.c (maintenance_set_test_settings_cmd): Remove. (maintenance_show_test_settings_cmd): Remove. (_initialize_maint_test_settings): Use add_basic_prefix_cmd, add_show_prefix_cmd. * maint-test-options.c (maintenance_test_options_command): Remove. (_initialize_maint_test_options): Use add_basic_prefix_cmd. * macrocmd.c (macro_command): Remove (_initialize_macrocmd): Use add_basic_prefix_cmd. * language.c (set_check, show_check): Remove. (_initialize_language): Use add_basic_prefix_cmd, add_show_prefix_cmd. * infcmd.c (unset_command): Remove. (_initialize_infcmd): Use add_basic_prefix_cmd. * i386-tdep.c (set_mpx_cmd, show_mpx_cmd): Remove. (_initialize_i386_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * go32-nat.c (go32_info_dos_command): Remove. (_initialize_go32_nat): Use add_basic_prefix_cmd. * cli/cli-decode.c (do_prefix_cmd, add_basic_prefix_cmd) (do_show_prefix_cmd, add_show_prefix_cmd): New functions. * frame.c (set_backtrace_cmd, show_backtrace_cmd): Remove. (_initialize_frame): Use add_basic_prefix_cmd, add_show_prefix_cmd. * dcache.c (set_dcache_command, show_dcache_command): Remove. (_initialize_dcache): Use add_basic_prefix_cmd, add_show_prefix_cmd. * cp-support.c (maint_cplus_command): Remove. (_initialize_cp_support): Use add_basic_prefix_cmd. * btrace.c (maint_btrace_cmd, maint_btrace_set_cmd) (maint_btrace_show_cmd, maint_btrace_pt_set_cmd) (maint_btrace_pt_show_cmd, _initialize_btrace): Use add_basic_prefix_cmd, add_show_prefix_cmd. * breakpoint.c (save_command): Remove. (_initialize_breakpoint): Use add_basic_prefix_cmd. * arm-tdep.c (set_arm_command, show_arm_command): Remove. (_initialize_arm_tdep): Use add_basic_prefix_cmd, add_show_prefix_cmd. * ada-lang.c (maint_set_ada_cmd, maint_show_ada_cmd) (set_ada_command, show_ada_command): Remove. (_initialize_ada_language): Use add_basic_prefix_cmd, add_show_prefix_cmd. * command.h (add_basic_prefix_cmd, add_show_prefix_cmd): Declare. gdb/testsuite/ChangeLog 2020-04-17 Tom Tromey <tromey@adacore.com> * gdb.cp/maint.exp (test_help): Simplify multiple_help_body. Update tests. * gdb.btrace/cpu.exp: Update tests. * gdb.base/maint.exp: Update tests. * gdb.base/default.exp: Update tests. * gdb.base/completion.exp: Update tests.
723 lines
19 KiB
C
723 lines
19 KiB
C
/* Caching code for GDB, the GNU debugger.
|
|
|
|
Copyright (C) 1992-2020 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 "defs.h"
|
|
#include "dcache.h"
|
|
#include "gdbcmd.h"
|
|
#include "gdbcore.h"
|
|
#include "target-dcache.h"
|
|
#include "inferior.h"
|
|
#include "splay-tree.h"
|
|
#include "gdbarch.h"
|
|
|
|
/* Commands with a prefix of `{set,show} dcache'. */
|
|
static struct cmd_list_element *dcache_set_list = NULL;
|
|
static struct cmd_list_element *dcache_show_list = NULL;
|
|
|
|
/* The data cache could lead to incorrect results because it doesn't
|
|
know about volatile variables, thus making it impossible to debug
|
|
functions which use memory mapped I/O devices. Set the nocache
|
|
memory region attribute in those cases.
|
|
|
|
In general the dcache speeds up performance. Some speed improvement
|
|
comes from the actual caching mechanism, but the major gain is in
|
|
the reduction of the remote protocol overhead; instead of reading
|
|
or writing a large area of memory in 4 byte requests, the cache
|
|
bundles up the requests into LINE_SIZE chunks, reducing overhead
|
|
significantly. This is most useful when accessing a large amount
|
|
of data, such as when performing a backtrace.
|
|
|
|
The cache is a splay tree along with a linked list for replacement.
|
|
Each block caches a LINE_SIZE area of memory. Within each line we
|
|
remember the address of the line (which must be a multiple of
|
|
LINE_SIZE) and the actual data block.
|
|
|
|
Lines are only allocated as needed, so DCACHE_SIZE really specifies the
|
|
*maximum* number of lines in the cache.
|
|
|
|
At present, the cache is write-through rather than writeback: as soon
|
|
as data is written to the cache, it is also immediately written to
|
|
the target. Therefore, cache lines are never "dirty". Whether a given
|
|
line is valid or not depends on where it is stored in the dcache_struct;
|
|
there is no per-block valid flag. */
|
|
|
|
/* NOTE: Interaction of dcache and memory region attributes
|
|
|
|
As there is no requirement that memory region attributes be aligned
|
|
to or be a multiple of the dcache page size, dcache_read_line() and
|
|
dcache_write_line() must break up the page by memory region. If a
|
|
chunk does not have the cache attribute set, an invalid memory type
|
|
is set, etc., then the chunk is skipped. Those chunks are handled
|
|
in target_xfer_memory() (or target_xfer_memory_partial()).
|
|
|
|
This doesn't occur very often. The most common occurrence is when
|
|
the last bit of the .text segment and the first bit of the .data
|
|
segment fall within the same dcache page with a ro/cacheable memory
|
|
region defined for the .text segment and a rw/non-cacheable memory
|
|
region defined for the .data segment. */
|
|
|
|
/* The maximum number of lines stored. The total size of the cache is
|
|
equal to DCACHE_SIZE times LINE_SIZE. */
|
|
#define DCACHE_DEFAULT_SIZE 4096
|
|
static unsigned dcache_size = DCACHE_DEFAULT_SIZE;
|
|
|
|
/* The default size of a cache line. Smaller values reduce the time taken to
|
|
read a single byte and make the cache more granular, but increase
|
|
overhead and reduce the effectiveness of the cache as a prefetcher. */
|
|
#define DCACHE_DEFAULT_LINE_SIZE 64
|
|
static unsigned dcache_line_size = DCACHE_DEFAULT_LINE_SIZE;
|
|
|
|
/* Each cache block holds LINE_SIZE bytes of data
|
|
starting at a multiple-of-LINE_SIZE address. */
|
|
|
|
#define LINE_SIZE_MASK(dcache) ((dcache->line_size - 1))
|
|
#define XFORM(dcache, x) ((x) & LINE_SIZE_MASK (dcache))
|
|
#define MASK(dcache, x) ((x) & ~LINE_SIZE_MASK (dcache))
|
|
|
|
struct dcache_block
|
|
{
|
|
/* For least-recently-allocated and free lists. */
|
|
struct dcache_block *prev;
|
|
struct dcache_block *next;
|
|
|
|
CORE_ADDR addr; /* address of data */
|
|
int refs; /* # hits */
|
|
gdb_byte data[1]; /* line_size bytes at given address */
|
|
};
|
|
|
|
struct dcache_struct
|
|
{
|
|
splay_tree tree;
|
|
struct dcache_block *oldest; /* least-recently-allocated list. */
|
|
|
|
/* The free list is maintained identically to OLDEST to simplify
|
|
the code: we only need one set of accessors. */
|
|
struct dcache_block *freelist;
|
|
|
|
/* The number of in-use lines in the cache. */
|
|
int size;
|
|
CORE_ADDR line_size; /* current line_size. */
|
|
|
|
/* The ptid of last inferior to use cache or null_ptid. */
|
|
ptid_t ptid;
|
|
};
|
|
|
|
typedef void (block_func) (struct dcache_block *block, void *param);
|
|
|
|
static struct dcache_block *dcache_hit (DCACHE *dcache, CORE_ADDR addr);
|
|
|
|
static int dcache_read_line (DCACHE *dcache, struct dcache_block *db);
|
|
|
|
static struct dcache_block *dcache_alloc (DCACHE *dcache, CORE_ADDR addr);
|
|
|
|
static bool dcache_enabled_p = false; /* OBSOLETE */
|
|
|
|
static void
|
|
show_dcache_enabled_p (struct ui_file *file, int from_tty,
|
|
struct cmd_list_element *c, const char *value)
|
|
{
|
|
fprintf_filtered (file, _("Deprecated remotecache flag is %s.\n"), value);
|
|
}
|
|
|
|
/* Add BLOCK to circular block list BLIST, behind the block at *BLIST.
|
|
*BLIST is not updated (unless it was previously NULL of course).
|
|
This is for the least-recently-allocated list's sake:
|
|
BLIST points to the oldest block.
|
|
??? This makes for poor cache usage of the free list,
|
|
but is it measurable? */
|
|
|
|
static void
|
|
append_block (struct dcache_block **blist, struct dcache_block *block)
|
|
{
|
|
if (*blist)
|
|
{
|
|
block->next = *blist;
|
|
block->prev = (*blist)->prev;
|
|
block->prev->next = block;
|
|
(*blist)->prev = block;
|
|
/* We don't update *BLIST here to maintain the invariant that for the
|
|
least-recently-allocated list *BLIST points to the oldest block. */
|
|
}
|
|
else
|
|
{
|
|
block->next = block;
|
|
block->prev = block;
|
|
*blist = block;
|
|
}
|
|
}
|
|
|
|
/* Remove BLOCK from circular block list BLIST. */
|
|
|
|
static void
|
|
remove_block (struct dcache_block **blist, struct dcache_block *block)
|
|
{
|
|
if (block->next == block)
|
|
{
|
|
*blist = NULL;
|
|
}
|
|
else
|
|
{
|
|
block->next->prev = block->prev;
|
|
block->prev->next = block->next;
|
|
/* If we removed the block *BLIST points to, shift it to the next block
|
|
to maintain the invariant that for the least-recently-allocated list
|
|
*BLIST points to the oldest block. */
|
|
if (*blist == block)
|
|
*blist = block->next;
|
|
}
|
|
}
|
|
|
|
/* Iterate over all elements in BLIST, calling FUNC.
|
|
PARAM is passed to FUNC.
|
|
FUNC may remove the block it's passed, but only that block. */
|
|
|
|
static void
|
|
for_each_block (struct dcache_block **blist, block_func *func, void *param)
|
|
{
|
|
struct dcache_block *db;
|
|
|
|
if (*blist == NULL)
|
|
return;
|
|
|
|
db = *blist;
|
|
do
|
|
{
|
|
struct dcache_block *next = db->next;
|
|
|
|
func (db, param);
|
|
db = next;
|
|
}
|
|
while (*blist && db != *blist);
|
|
}
|
|
|
|
/* BLOCK_FUNC routine for dcache_free. */
|
|
|
|
static void
|
|
free_block (struct dcache_block *block, void *param)
|
|
{
|
|
xfree (block);
|
|
}
|
|
|
|
/* Free a data cache. */
|
|
|
|
void
|
|
dcache_free (DCACHE *dcache)
|
|
{
|
|
splay_tree_delete (dcache->tree);
|
|
for_each_block (&dcache->oldest, free_block, NULL);
|
|
for_each_block (&dcache->freelist, free_block, NULL);
|
|
xfree (dcache);
|
|
}
|
|
|
|
|
|
/* BLOCK_FUNC function for dcache_invalidate.
|
|
This doesn't remove the block from the oldest list on purpose.
|
|
dcache_invalidate will do it later. */
|
|
|
|
static void
|
|
invalidate_block (struct dcache_block *block, void *param)
|
|
{
|
|
DCACHE *dcache = (DCACHE *) param;
|
|
|
|
splay_tree_remove (dcache->tree, (splay_tree_key) block->addr);
|
|
append_block (&dcache->freelist, block);
|
|
}
|
|
|
|
/* Free all the data cache blocks, thus discarding all cached data. */
|
|
|
|
void
|
|
dcache_invalidate (DCACHE *dcache)
|
|
{
|
|
for_each_block (&dcache->oldest, invalidate_block, dcache);
|
|
|
|
dcache->oldest = NULL;
|
|
dcache->size = 0;
|
|
dcache->ptid = null_ptid;
|
|
|
|
if (dcache->line_size != dcache_line_size)
|
|
{
|
|
/* We've been asked to use a different line size.
|
|
All of our freelist blocks are now the wrong size, so free them. */
|
|
|
|
for_each_block (&dcache->freelist, free_block, dcache);
|
|
dcache->freelist = NULL;
|
|
dcache->line_size = dcache_line_size;
|
|
}
|
|
}
|
|
|
|
/* Invalidate the line associated with ADDR. */
|
|
|
|
static void
|
|
dcache_invalidate_line (DCACHE *dcache, CORE_ADDR addr)
|
|
{
|
|
struct dcache_block *db = dcache_hit (dcache, addr);
|
|
|
|
if (db)
|
|
{
|
|
splay_tree_remove (dcache->tree, (splay_tree_key) db->addr);
|
|
remove_block (&dcache->oldest, db);
|
|
append_block (&dcache->freelist, db);
|
|
--dcache->size;
|
|
}
|
|
}
|
|
|
|
/* If addr is present in the dcache, return the address of the block
|
|
containing it. Otherwise return NULL. */
|
|
|
|
static struct dcache_block *
|
|
dcache_hit (DCACHE *dcache, CORE_ADDR addr)
|
|
{
|
|
struct dcache_block *db;
|
|
|
|
splay_tree_node node = splay_tree_lookup (dcache->tree,
|
|
(splay_tree_key) MASK (dcache, addr));
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
db = (struct dcache_block *) node->value;
|
|
db->refs++;
|
|
return db;
|
|
}
|
|
|
|
/* Fill a cache line from target memory.
|
|
The result is 1 for success, 0 if the (entire) cache line
|
|
wasn't readable. */
|
|
|
|
static int
|
|
dcache_read_line (DCACHE *dcache, struct dcache_block *db)
|
|
{
|
|
CORE_ADDR memaddr;
|
|
gdb_byte *myaddr;
|
|
int len;
|
|
int res;
|
|
int reg_len;
|
|
struct mem_region *region;
|
|
|
|
len = dcache->line_size;
|
|
memaddr = db->addr;
|
|
myaddr = db->data;
|
|
|
|
while (len > 0)
|
|
{
|
|
/* Don't overrun if this block is right at the end of the region. */
|
|
region = lookup_mem_region (memaddr);
|
|
if (region->hi == 0 || memaddr + len < region->hi)
|
|
reg_len = len;
|
|
else
|
|
reg_len = region->hi - memaddr;
|
|
|
|
/* Skip non-readable regions. The cache attribute can be ignored,
|
|
since we may be loading this for a stack access. */
|
|
if (region->attrib.mode == MEM_WO)
|
|
{
|
|
memaddr += reg_len;
|
|
myaddr += reg_len;
|
|
len -= reg_len;
|
|
continue;
|
|
}
|
|
|
|
res = target_read_raw_memory (memaddr, myaddr, reg_len);
|
|
if (res != 0)
|
|
return 0;
|
|
|
|
memaddr += reg_len;
|
|
myaddr += reg_len;
|
|
len -= reg_len;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Get a free cache block, put or keep it on the valid list,
|
|
and return its address. */
|
|
|
|
static struct dcache_block *
|
|
dcache_alloc (DCACHE *dcache, CORE_ADDR addr)
|
|
{
|
|
struct dcache_block *db;
|
|
|
|
if (dcache->size >= dcache_size)
|
|
{
|
|
/* Evict the least recently allocated line. */
|
|
db = dcache->oldest;
|
|
remove_block (&dcache->oldest, db);
|
|
|
|
splay_tree_remove (dcache->tree, (splay_tree_key) db->addr);
|
|
}
|
|
else
|
|
{
|
|
db = dcache->freelist;
|
|
if (db)
|
|
remove_block (&dcache->freelist, db);
|
|
else
|
|
db = ((struct dcache_block *)
|
|
xmalloc (offsetof (struct dcache_block, data)
|
|
+ dcache->line_size));
|
|
|
|
dcache->size++;
|
|
}
|
|
|
|
db->addr = MASK (dcache, addr);
|
|
db->refs = 0;
|
|
|
|
/* Put DB at the end of the list, it's the newest. */
|
|
append_block (&dcache->oldest, db);
|
|
|
|
splay_tree_insert (dcache->tree, (splay_tree_key) db->addr,
|
|
(splay_tree_value) db);
|
|
|
|
return db;
|
|
}
|
|
|
|
/* Using the data cache DCACHE, store in *PTR the contents of the byte at
|
|
address ADDR in the remote machine.
|
|
|
|
Returns 1 for success, 0 for error. */
|
|
|
|
static int
|
|
dcache_peek_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr)
|
|
{
|
|
struct dcache_block *db = dcache_hit (dcache, addr);
|
|
|
|
if (!db)
|
|
{
|
|
db = dcache_alloc (dcache, addr);
|
|
|
|
if (!dcache_read_line (dcache, db))
|
|
return 0;
|
|
}
|
|
|
|
*ptr = db->data[XFORM (dcache, addr)];
|
|
return 1;
|
|
}
|
|
|
|
/* Write the byte at PTR into ADDR in the data cache.
|
|
|
|
The caller should have written the data through to target memory
|
|
already.
|
|
|
|
If ADDR is not in cache, this function does nothing; writing to an
|
|
area of memory which wasn't present in the cache doesn't cause it
|
|
to be loaded in. */
|
|
|
|
static void
|
|
dcache_poke_byte (DCACHE *dcache, CORE_ADDR addr, const gdb_byte *ptr)
|
|
{
|
|
struct dcache_block *db = dcache_hit (dcache, addr);
|
|
|
|
if (db)
|
|
db->data[XFORM (dcache, addr)] = *ptr;
|
|
}
|
|
|
|
static int
|
|
dcache_splay_tree_compare (splay_tree_key a, splay_tree_key b)
|
|
{
|
|
if (a > b)
|
|
return 1;
|
|
else if (a == b)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/* Allocate and initialize a data cache. */
|
|
|
|
DCACHE *
|
|
dcache_init (void)
|
|
{
|
|
DCACHE *dcache = XNEW (DCACHE);
|
|
|
|
dcache->tree = splay_tree_new (dcache_splay_tree_compare,
|
|
NULL,
|
|
NULL);
|
|
|
|
dcache->oldest = NULL;
|
|
dcache->freelist = NULL;
|
|
dcache->size = 0;
|
|
dcache->line_size = dcache_line_size;
|
|
dcache->ptid = null_ptid;
|
|
|
|
return dcache;
|
|
}
|
|
|
|
|
|
/* Read LEN bytes from dcache memory at MEMADDR, transferring to
|
|
debugger address MYADDR. If the data is presently cached, this
|
|
fills the cache. Arguments/return are like the target_xfer_partial
|
|
interface. */
|
|
|
|
enum target_xfer_status
|
|
dcache_read_memory_partial (struct target_ops *ops, DCACHE *dcache,
|
|
CORE_ADDR memaddr, gdb_byte *myaddr,
|
|
ULONGEST len, ULONGEST *xfered_len)
|
|
{
|
|
ULONGEST i;
|
|
|
|
/* If this is a different inferior from what we've recorded,
|
|
flush the cache. */
|
|
|
|
if (inferior_ptid != dcache->ptid)
|
|
{
|
|
dcache_invalidate (dcache);
|
|
dcache->ptid = inferior_ptid;
|
|
}
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
if (!dcache_peek_byte (dcache, memaddr + i, myaddr + i))
|
|
{
|
|
/* That failed. Discard its cache line so we don't have a
|
|
partially read line. */
|
|
dcache_invalidate_line (dcache, memaddr + i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == 0)
|
|
{
|
|
/* Even though reading the whole line failed, we may be able to
|
|
read a piece starting where the caller wanted. */
|
|
return raw_memory_xfer_partial (ops, myaddr, NULL, memaddr, len,
|
|
xfered_len);
|
|
}
|
|
else
|
|
{
|
|
*xfered_len = i;
|
|
return TARGET_XFER_OK;
|
|
}
|
|
}
|
|
|
|
/* FIXME: There would be some benefit to making the cache write-back and
|
|
moving the writeback operation to a higher layer, as it could occur
|
|
after a sequence of smaller writes have been completed (as when a stack
|
|
frame is constructed for an inferior function call). Note that only
|
|
moving it up one level to target_xfer_memory[_partial]() is not
|
|
sufficient since we want to coalesce memory transfers that are
|
|
"logically" connected but not actually a single call to one of the
|
|
memory transfer functions. */
|
|
|
|
/* Just update any cache lines which are already present. This is
|
|
called by the target_xfer_partial machinery when writing raw
|
|
memory. */
|
|
|
|
void
|
|
dcache_update (DCACHE *dcache, enum target_xfer_status status,
|
|
CORE_ADDR memaddr, const gdb_byte *myaddr,
|
|
ULONGEST len)
|
|
{
|
|
ULONGEST i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
if (status == TARGET_XFER_OK)
|
|
dcache_poke_byte (dcache, memaddr + i, myaddr + i);
|
|
else
|
|
{
|
|
/* Discard the whole cache line so we don't have a partially
|
|
valid line. */
|
|
dcache_invalidate_line (dcache, memaddr + i);
|
|
}
|
|
}
|
|
|
|
/* Print DCACHE line INDEX. */
|
|
|
|
static void
|
|
dcache_print_line (DCACHE *dcache, int index)
|
|
{
|
|
splay_tree_node n;
|
|
struct dcache_block *db;
|
|
int i, j;
|
|
|
|
if (dcache == NULL)
|
|
{
|
|
printf_filtered (_("No data cache available.\n"));
|
|
return;
|
|
}
|
|
|
|
n = splay_tree_min (dcache->tree);
|
|
|
|
for (i = index; i > 0; --i)
|
|
{
|
|
if (!n)
|
|
break;
|
|
n = splay_tree_successor (dcache->tree, n->key);
|
|
}
|
|
|
|
if (!n)
|
|
{
|
|
printf_filtered (_("No such cache line exists.\n"));
|
|
return;
|
|
}
|
|
|
|
db = (struct dcache_block *) n->value;
|
|
|
|
printf_filtered (_("Line %d: address %s [%d hits]\n"),
|
|
index, paddress (target_gdbarch (), db->addr), db->refs);
|
|
|
|
for (j = 0; j < dcache->line_size; j++)
|
|
{
|
|
printf_filtered ("%02x ", db->data[j]);
|
|
|
|
/* Print a newline every 16 bytes (48 characters). */
|
|
if ((j % 16 == 15) && (j != dcache->line_size - 1))
|
|
printf_filtered ("\n");
|
|
}
|
|
printf_filtered ("\n");
|
|
}
|
|
|
|
/* Parse EXP and show the info about DCACHE. */
|
|
|
|
static void
|
|
dcache_info_1 (DCACHE *dcache, const char *exp)
|
|
{
|
|
splay_tree_node n;
|
|
int i, refcount;
|
|
|
|
if (exp)
|
|
{
|
|
char *linestart;
|
|
|
|
i = strtol (exp, &linestart, 10);
|
|
if (linestart == exp || i < 0)
|
|
{
|
|
printf_filtered (_("Usage: info dcache [LINENUMBER]\n"));
|
|
return;
|
|
}
|
|
|
|
dcache_print_line (dcache, i);
|
|
return;
|
|
}
|
|
|
|
printf_filtered (_("Dcache %u lines of %u bytes each.\n"),
|
|
dcache_size,
|
|
dcache ? (unsigned) dcache->line_size
|
|
: dcache_line_size);
|
|
|
|
if (dcache == NULL || dcache->ptid == null_ptid)
|
|
{
|
|
printf_filtered (_("No data cache available.\n"));
|
|
return;
|
|
}
|
|
|
|
printf_filtered (_("Contains data for %s\n"),
|
|
target_pid_to_str (dcache->ptid).c_str ());
|
|
|
|
refcount = 0;
|
|
|
|
n = splay_tree_min (dcache->tree);
|
|
i = 0;
|
|
|
|
while (n)
|
|
{
|
|
struct dcache_block *db = (struct dcache_block *) n->value;
|
|
|
|
printf_filtered (_("Line %d: address %s [%d hits]\n"),
|
|
i, paddress (target_gdbarch (), db->addr), db->refs);
|
|
i++;
|
|
refcount += db->refs;
|
|
|
|
n = splay_tree_successor (dcache->tree, n->key);
|
|
}
|
|
|
|
printf_filtered (_("Cache state: %d active lines, %d hits\n"), i, refcount);
|
|
}
|
|
|
|
static void
|
|
info_dcache_command (const char *exp, int tty)
|
|
{
|
|
dcache_info_1 (target_dcache_get (), exp);
|
|
}
|
|
|
|
static void
|
|
set_dcache_size (const char *args, int from_tty,
|
|
struct cmd_list_element *c)
|
|
{
|
|
if (dcache_size == 0)
|
|
{
|
|
dcache_size = DCACHE_DEFAULT_SIZE;
|
|
error (_("Dcache size must be greater than 0."));
|
|
}
|
|
target_dcache_invalidate ();
|
|
}
|
|
|
|
static void
|
|
set_dcache_line_size (const char *args, int from_tty,
|
|
struct cmd_list_element *c)
|
|
{
|
|
if (dcache_line_size < 2
|
|
|| (dcache_line_size & (dcache_line_size - 1)) != 0)
|
|
{
|
|
unsigned d = dcache_line_size;
|
|
dcache_line_size = DCACHE_DEFAULT_LINE_SIZE;
|
|
error (_("Invalid dcache line size: %u (must be power of 2)."), d);
|
|
}
|
|
target_dcache_invalidate ();
|
|
}
|
|
|
|
void _initialize_dcache ();
|
|
void
|
|
_initialize_dcache ()
|
|
{
|
|
add_setshow_boolean_cmd ("remotecache", class_support,
|
|
&dcache_enabled_p, _("\
|
|
Set cache use for remote targets."), _("\
|
|
Show cache use for remote targets."), _("\
|
|
This used to enable the data cache for remote targets. The cache\n\
|
|
functionality is now controlled by the memory region system and the\n\
|
|
\"stack-cache\" flag; \"remotecache\" now does nothing and\n\
|
|
exists only for compatibility reasons."),
|
|
NULL,
|
|
show_dcache_enabled_p,
|
|
&setlist, &showlist);
|
|
|
|
add_info ("dcache", info_dcache_command,
|
|
_("\
|
|
Print information on the dcache performance.\n\
|
|
Usage: info dcache [LINENUMBER]\n\
|
|
With no arguments, this command prints the cache configuration and a\n\
|
|
summary of each line in the cache. With an argument, dump\"\n\
|
|
the contents of the given line."));
|
|
|
|
add_basic_prefix_cmd ("dcache", class_obscure, _("\
|
|
Use this command to set number of lines in dcache and line-size."),
|
|
&dcache_set_list, "set dcache ", /*allow_unknown*/0,
|
|
&setlist);
|
|
add_show_prefix_cmd ("dcache", class_obscure, _("\
|
|
Show dcachesettings."),
|
|
&dcache_show_list, "show dcache ", /*allow_unknown*/0,
|
|
&showlist);
|
|
|
|
add_setshow_zuinteger_cmd ("line-size", class_obscure,
|
|
&dcache_line_size, _("\
|
|
Set dcache line size in bytes (must be power of 2)."), _("\
|
|
Show dcache line size."),
|
|
NULL,
|
|
set_dcache_line_size,
|
|
NULL,
|
|
&dcache_set_list, &dcache_show_list);
|
|
add_setshow_zuinteger_cmd ("size", class_obscure,
|
|
&dcache_size, _("\
|
|
Set number of dcache lines."), _("\
|
|
Show number of dcache lines."),
|
|
NULL,
|
|
set_dcache_size,
|
|
NULL,
|
|
&dcache_set_list, &dcache_show_list);
|
|
}
|