From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 28016 invoked by alias); 3 Nov 2005 01:35:29 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 27644 invoked by uid 22791); 3 Nov 2005 01:35:24 -0000 Received: from sj-iport-3-in.cisco.com (HELO sj-iport-3.cisco.com) (171.71.176.72) by sourceware.org (qpsmtpd/0.30-dev) with ESMTP; Thu, 03 Nov 2005 01:35:24 +0000 Received: from sj-core-5.cisco.com ([171.71.177.238]) by sj-iport-3.cisco.com with ESMTP; 02 Nov 2005 17:35:23 -0800 X-IronPort-AV: i="3.97,283,1125903600"; d="diff'?scan'208"; a="360123614:sNHT35357928" Received: from xbh-sjc-221.amer.cisco.com (xbh-sjc-221.cisco.com [128.107.191.63]) by sj-core-5.cisco.com (8.12.10/8.12.6) with ESMTP id jA31Z3TX012935; Wed, 2 Nov 2005 17:35:20 -0800 (PST) Received: from xfe-sjc-212.amer.cisco.com ([171.70.151.187]) by xbh-sjc-221.amer.cisco.com with Microsoft SMTPSVC(6.0.3790.211); Wed, 2 Nov 2005 17:35:16 -0800 Received: from [128.107.165.235] ([128.107.165.235]) by xfe-sjc-212.amer.cisco.com with Microsoft SMTPSVC(6.0.3790.211); Wed, 2 Nov 2005 17:35:15 -0800 Message-ID: <43696953.9090601@cisco.com> Date: Thu, 03 Nov 2005 01:55:00 -0000 From: Michael Snyder User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.3) Gecko/20040929 MIME-Version: 1.0 To: gdb@sources.redhat.com, gdb-patches@sources.redhat.com Subject: [RFC] a prototype checkpoint-restart using core files Content-Type: multipart/mixed; boundary="------------080009050904060202050607" X-SW-Source: 2005-11/txt/msg00053.txt.bz2 This is a multi-part message in MIME format. --------------080009050904060202050607 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Content-length: 1519 Folks, this isn't for commit, just for discussion. Attached is an experimental patch that adds a command "restore-core-file" or "rcore", which is the inverse of "generate-core-file" (gcore). Instead of copying the memory and register state of a process into a file, it takes an existing corefile, and copies its memory and register state into the child process. The idea was to experiment with the concept of doing checkpoint and restore, by using a corefile as the checkpoint file. Obviously it has limitations -- it doesn't save any kernel state, I/O state etc. Just user state. But it turns out that if you avoid those limitations, it works! As a conservative rule of thumb, you can go back to an earlier state so long as you don't cross a system call. And in practice there are lots of system calls that can be regarded as "stateless", or that change only user state -- so you can cross those. I've only tried it on Linux, but it seems to me that it should be pretty portable, at least to hosts for which 'gcore' works. This may be useful for anyone who wants to experiment with the idea of checkpoint and restart, get a feel for how it would work in practice, and look for "gotchas" where the sudden state change might interfere with something else in gdb. For a "real" checkpoint and restart facility, we would probably want to handle some amount of kernel state and I/O state, but gcore/rcore might even be somewhat useful as-is (at least to a knowledgeable user who understands their limitations). --------------080009050904060202050607 Content-Type: text/plain; name="rcore.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="rcore.diff" Content-length: 4948 Index: gcore.c =================================================================== RCS file: /cvs/src/src/gdb/gcore.c,v retrieving revision 1.17 diff -p -r1.17 gcore.c *** gcore.c 15 Feb 2005 15:49:10 -0000 1.17 --- gcore.c 3 Nov 2005 01:19:53 -0000 *************** gcore_memory_sections (bfd *obfd) *** 488,493 **** --- 488,621 ---- return 1; } + /* OK now, I want to add a new command to read a corefile, + and restore its state into the inferior process. Obviously + dangerous, probably want to make certain that they are + actually the same process! But we can put that off till + later. Let's see what's required. This should actually + be pretty easy. */ + + static void + load_core_sections (bfd *abfd, asection *asect, void *arg) + { + unsigned long from_tty = (unsigned long) arg; + char *memhunk; + + if ((bfd_section_size (abfd, asect) > 0) && + (bfd_get_section_flags (abfd, asect) & SEC_LOAD)) + { + if (info_verbose && from_tty) + { + printf_filtered (_("Load core section %s"), + bfd_section_name (abfd, asect)); + printf_filtered (_(", address 0x%08lx"), + (unsigned long) bfd_section_vma (abfd, asect)); + printf_filtered (_(", size = %d"), + (int) bfd_section_size (abfd, asect)); + printf_filtered (_(".\n")); + } + /* Fixme cleanup? */ + memhunk = xmalloc (bfd_section_size (abfd, asect)); + bfd_get_section_contents (abfd, asect, memhunk, 0, + bfd_section_size (abfd, asect)); + target_write_memory (bfd_section_vma (abfd, asect), + memhunk, + bfd_section_size (abfd, asect)); + xfree (memhunk); + } + } + + #include + #ifndef O_BINARY + #define O_BINARY 0 + #endif + + #include "regcache.h" + #include "regset.h" + + static void + rcore_command (char *args, int from_tty) + { + /* corelow.c core_open */ + /* scratch_chan = open (filename) + temp_bfd = bfd_fdopenr (filename, gnutarget, scratch_chan) + bfd_check_format (temp_bfd, bfd_core) + build_section_table (core_bfd, to_sections, to_sections_end) + bfd_map_over_sections (core_bfd, myfunc) + myfunc will check for loadable, contents, and size, + and then write the section contents into memory at vma. + */ + char *corefilename, corefilename_buffer[40], *scratch_path; + int scratch_chan; + bfd *core_bfd; + + /* Can't restore a corefile without a target process. */ + if (!target_has_execution) + noprocess (); + + if (args && *args) + corefilename = args; + else + { + /* Default corefile name is "core.PID". */ + sprintf (corefilename_buffer, "core.%d", PIDGET (inferior_ptid)); + corefilename = corefilename_buffer; + } + + if (info_verbose) + fprintf_filtered (gdb_stdout, + _("Opening corefile '%s' for input.\n"), corefilename); + + scratch_chan = openp (getenv ("PATH"), OPF_TRY_CWD_FIRST, corefilename, + O_BINARY | O_RDONLY | O_LARGEFILE, 0, &scratch_path); + if (scratch_chan < 0) + perror_with_name (corefilename); + + core_bfd = bfd_fdopenr (scratch_path, gnutarget, scratch_chan); + if (!core_bfd) + perror_with_name (scratch_path); + + if (!bfd_check_format (core_bfd, bfd_core)) + { + make_cleanup_bfd_close (core_bfd); + error (_("\"%s\" is not a core file: %s"), + corefilename, bfd_errmsg (bfd_get_error ())); + } + + bfd_map_over_sections (core_bfd, load_core_sections, (void *) from_tty); + /* Now need to get/set registers. */ + { + struct bfd_section *regsect = bfd_get_section_by_name (core_bfd, ".reg"); + char *contents; + int size; + + if (!regsect) + error (_("Couldn't find .reg section.")); + + size = bfd_section_size (core_bfd, regsect); + contents = xmalloc (size); + bfd_get_section_contents (core_bfd, regsect, contents, 0, size); + + if (gdbarch_regset_from_core_section_p (current_gdbarch)) + { + const struct regset *regset; + + regset = gdbarch_regset_from_core_section (current_gdbarch, + ".reg", size); + if (!regset) + error (_("Failed to allocate regset.")); + + registers_changed (); + regset->supply_regset (regset, current_regcache, + -1, contents, size); + reinit_frame_cache (); + target_store_registers (-1); + } + } + + bfd_close (core_bfd); + } + void _initialize_gcore (void) { *************** Argument is optional filename. Default *** 497,500 **** --- 625,633 ---- add_com_alias ("gcore", "generate-core-file", class_files, 1); exec_set_find_memory_regions (objfile_find_memory_regions); + + add_com ("restore-core-file", class_files, rcore_command, _("\ + Restore the machine state from a core file into the debugged process.\n\ + Argument is optional filename. Default filename is 'core.'.")); + add_com_alias ("rcore", "restore-core-file", class_files, 1); } --------------080009050904060202050607--