From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 25220 invoked by alias); 5 Aug 2009 21:23:21 -0000 Received: (qmail 25211 invoked by uid 22791); 5 Aug 2009 21:23:19 -0000 X-SWARE-Spam-Status: No, hits=-2.4 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: sourceware.org Received: from smtp-outbound-2.vmware.com (HELO smtp-outbound-2.vmware.com) (65.115.85.73) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 05 Aug 2009 21:23:13 +0000 Received: from mailhost3.vmware.com (mailhost3.vmware.com [10.16.27.45]) by smtp-outbound-2.vmware.com (Postfix) with ESMTP id 6A5251F008; Wed, 5 Aug 2009 14:23:11 -0700 (PDT) Received: from [10.20.94.141] (msnyder-server.eng.vmware.com [10.20.94.141]) by mailhost3.vmware.com (Postfix) with ESMTP id 6020BCD903; Wed, 5 Aug 2009 14:23:11 -0700 (PDT) Message-ID: <4A79F802.4060102@vmware.com> Date: Wed, 05 Aug 2009 21:23:00 -0000 From: Michael Snyder User-Agent: Thunderbird 1.5.0.12 (X11/20080411) MIME-Version: 1.0 To: Hui Zhu CC: Eli Zaretskii , "gdb-patches@sourceware.org" Subject: Re: [RFA/RFC] Add dump and load command to process record and replay References: <4A749572.7030005@vmware.com> <4A75054C.9030304@vmware.com> <83ws5koinl.fsf@gnu.org> <83my6fo2pa.fsf@gnu.org> <4A78935B.5030508@vmware.com> In-Reply-To: Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-08/txt/msg00080.txt.bz2 Hui Zhu wrote: > +static void > +cmd_record_fd_cleanups (void *recfdp) > +{ > + int recfd = *(int *) recfdp; > + close (recfd); > +} Here's a suggested comment to start documenting the file format: /* Record log save-file format Version 1 Header: 4 bytes: magic number RECORD_FILE_MAGIC. NOTE: be sure to change whenever this file format changes! Records: record_end: 1 byte: record type (record_end) record_reg: 1 byte: record type (record_reg) 8 bytes: register id 16 bytes: register value record_mem: 1 byte: record type (record_mem) 8 bytes: memory address 8 bytes: memory length n bytes: memory value (n == memory length) Version 2 (proposed) [...] */ Below I'll suggest some possible debugging printfs. Feel free to modify them to your own liking. > +static inline void > +record_read_dump (char *recfilename, int fildes, void *buf, size_t nbyte) > +{ > + if (read (fildes, buf, nbyte) != nbyte) > + error (_("Failed to read dump of execution records in '%s'."), > + recfilename); > +} > + > +static inline void > +record_write_dump (char *recfilename, int fildes, const void *buf, > + size_t nbyte) > +{ > + if (write (fildes, buf, nbyte) != nbyte) > + error (_("Failed to write dump of execution records to '%s'."), > + recfilename); > +} > + > +/* Dump the execution log to a file. */ > + > +static void > +cmd_record_dump (char *args, int from_tty) > +{ > + char *recfilename, recfilename_buffer[40]; > + int recfd; > + struct record_entry *cur_record_list; > + uint32_t magic; > + struct regcache *regcache; > + struct gdbarch *gdbarch; > + struct cleanup *old_cleanups; > + struct cleanup *set_cleanups; > + > + if (current_target.to_stratum != record_stratum) > + error (_("Process record is not started.\n")); > + > + if (args && *args) > + recfilename = args; > + else > + { > + /* Default corefile name is "gdb_record.PID". */ > + sprintf (recfilename_buffer, "gdb_record.%d", PIDGET (inferior_ptid)); > + recfilename = recfilename_buffer; > + } > + > + /* Open the dump file. */ if (record_debug) fprintf_unfiltered (gdb_stdlog, _("Saving recording to file '%s'\n"), recfilename); > + recfd = open (recfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, > + S_IRUSR | S_IWUSR); > + if (recfd < 0) > + error (_("Failed to open '%s' for dump execution records: %s"), > + recfilename, strerror (errno)); > + old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd); > + > + /* Save the current record entry to "cur_record_list". */ > + cur_record_list = record_list; > + > + /* Get the values of regcache and gdbarch. */ > + regcache = get_current_regcache (); > + gdbarch = get_regcache_arch (regcache); > + > + /* Disable the GDB operation record. */ > + set_cleanups = record_gdb_operation_disable_set (); > + > + /* Write the magic code. */ > + magic = RECORD_FILE_MAGIC; if (record_debug) fprintf_unfiltered (gdb_stdlog, _("\ Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), magic); > + record_write_dump (recfilename, recfd, &magic, 4); > + > + /* Reverse execute to the begin of record list. */ > + while (1) > + { > + /* Check for beginning and end of log. */ > + if (record_list == &record_first) > + break; > + > + record_exec_entry (regcache, gdbarch, record_list); > + > + if (record_list->prev) > + record_list = record_list->prev; > + } > + > + /* Dump the entries to recfd and forward execute to the end of > + record list. */ > + while (1) > + { > + /* Dump entry. */ > + if (record_list != &record_first) > + { > + uint8_t tmpu8; > + uint64_t tmpu64; > + > + tmpu8 = record_list->type; > + record_write_dump (recfilename, recfd, &tmpu8, 1); > + > + switch (record_list->type) > + { > + case record_reg: /* reg */ > + tmpu64 = record_list->u.reg.num; > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); if (record_debug) fprintf_unfiltered (gdb_stdlog, _("\ Writing register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"), p->u.reg.num, *(ULONGEST *) p->u.reg.val, MAX_REGISTER_SIZE); > + record_write_dump (recfilename, recfd, &tmpu64, 8); > + > + record_write_dump (recfilename, recfd, record_list->u.reg.val, > + MAX_REGISTER_SIZE); > + break; > + case record_mem: /* mem */ > + if (!record_list->u.mem.mem_entry_not_accessible) > + { > + tmpu64 = record_list->u.mem.addr; > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); if (record_debug) fprintf_unfiltered (gdb_stdlog, _("\ Writing memory 0x%08x (1 plus 8 plus 8 bytes plus %d bytes)\n"), (unsigned int) p->u.mem.addr, p->u.mem.len); > + record_write_dump (recfilename, recfd, &tmpu64, 8); > + > + tmpu64 = record_list->u.mem.len; > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + record_write_dump (recfilename, recfd, &tmpu64, 8); > + > + record_write_dump (recfilename, recfd, > + record_list->u.mem.val, > + record_list->u.mem.len); > + } > + break; case record_end: /* FIXME: record the contents of record_end rec. */ if (record_debug) fprintf_unfiltered (gdb_stdlog, _("\ Writing record_end (1 byte)\n")); break; > + } > + } > + > + /* Execute entry. */ > + record_exec_entry (regcache, gdbarch, record_list); > + > + if (record_list->next) > + record_list = record_list->next; > + else > + break; > + } > + > + /* Reverse execute to cur_record_list. */ > + while (1) > + { > + /* Check for beginning and end of log. */ > + if (record_list == cur_record_list) > + break; > + > + record_exec_entry (regcache, gdbarch, record_list); > + > + if (record_list->prev) > + record_list = record_list->prev; > + } > + > + do_cleanups (set_cleanups); > + do_cleanups (old_cleanups); > + > + /* Succeeded. */ > + fprintf_filtered (gdb_stdout, _("Saved dump of execution " > + "records to `%s'.\n"), > + recfilename); > +} > + > +/* Load the execution log from a file. */ > + > +static void > +cmd_record_load (char *args, int from_tty) > +{ > + int recfd; > + uint32_t magic; > + struct cleanup *old_cleanups; > + struct cleanup *old_cleanups2; > + struct record_entry *rec; > + int insn_number = 0; > + > + if (current_target.to_stratum != record_stratum) > + { > + cmd_record_start (NULL, from_tty); > + printf_unfiltered (_("Auto start process record.\n")); > + } > + > + if (!args || (args && !*args)) > + error (_("Argument for filename required.\n")); > + /* Open the load file. */ if (record_debug) fprintf_unfiltered (gdb_stdlog, _("Restoring recording from file '%s'\n"), args); > + /* Open the load file. */ > + recfd = open (args, O_RDONLY | O_BINARY); > + if (recfd < 0) > + error (_("Failed to open '%s' for loading execution records: %s"), > + args, strerror (errno)); > + old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd); > + > + /* Check the magic code. */ > + record_read_dump (args, recfd, &magic, 4); > + if (magic != RECORD_FILE_MAGIC) > + error (_("'%s' is not a valid dump of execution records."), args); > + > + /* Load the entries in recfd to the record_arch_list_head and > + record_arch_list_tail. */ > + record_arch_list_head = NULL; > + record_arch_list_tail = NULL; > + old_cleanups2 = make_cleanup (record_arch_list_cleanups, 0); > + > + while (1) > + { > + int ret; > + uint8_t tmpu8; > + uint64_t tmpu64; > + > + ret = read (recfd, &tmpu8, 1); > + if (ret < 0) > + error (_("Failed to read dump of execution records in '%s'."), args); > + if (ret == 0) > + break; > + > + switch (tmpu8) > + { > + case record_reg: /* reg */ > + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); > + rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE); > + rec->prev = NULL; > + rec->next = NULL; > + rec->type = record_reg; > + /* Get num. */ > + record_read_dump (args, recfd, &tmpu64, 8); > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + rec->u.reg.num = tmpu64; > + /* Get val. */ > + record_read_dump (args, recfd, rec->u.reg.val, MAX_REGISTER_SIZE); if (record_debug) fprintf_unfiltered (gdb_stdlog, _("\ Reading register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"), rec->u.reg.num, *(ULONGEST *) rec->u.reg.val, MAX_REGISTER_SIZE); > + record_arch_list_add (rec); > + break; > + case record_mem: /* mem */ > + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); > + rec->prev = NULL; > + rec->next = NULL; > + rec->type = record_mem; > + /* Get addr. */ > + record_read_dump (args, recfd, &tmpu64, 8); > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + rec->u.mem.addr = tmpu64; > + /* Get len. */ > + record_read_dump (args, recfd, &tmpu64, 8); > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + rec->u.mem.len = tmpu64; > + rec->u.mem.mem_entry_not_accessible = 0; > + rec->u.mem.val = (gdb_byte *) xmalloc (rec->u.mem.len); > + /* Get val. */ > + record_read_dump (args, recfd, rec->u.mem.val, rec->u.mem.len); if (record_debug) fprintf_unfiltered (gdb_stdlog, _("\ Reading memory 0x%08x (1 plus 8 plus %d bytes)\n"), (unsigned int) rec->u.mem.addr, rec->u.mem.len); > + record_arch_list_add (rec); > + break; > + > + case record_end: /* end */ > + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); > + rec->prev = NULL; > + rec->next = NULL; > + rec->type = record_end; if (record_debug) fprintf_unfiltered (gdb_stdlog, _("\ Reading record_end (one byte)\n")); > + record_arch_list_add (rec); > + insn_number ++; > + break; > + > + default: > + error (_("Format of '%s' is not right."), args); > + break; > + } > + } > + > + discard_cleanups (old_cleanups2); > + > + /* Add record_arch_list_head to the end of record list. */ > + for (rec = record_list; rec->next; rec = rec->next); > + rec->next = record_arch_list_head; > + record_arch_list_head->prev = rec; > + > + /* Update record_insn_num and record_insn_max_num. */ > + record_insn_num += insn_number; > + if (record_insn_num > record_insn_max_num) > + { > + record_insn_max_num = record_insn_num; > + warning (_("Auto increase record/replay buffer limit to %d."), > + record_insn_max_num); > + } > + > + do_cleanups (old_cleanups); > + > + /* Succeeded. */ > + fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", args); > +} > +