Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/checkpoint-restore/criu.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@virtuozzo.com>2016-06-01 14:32:00 +0300
committerPavel Emelyanov <xemul@virtuozzo.com>2016-06-28 13:04:58 +0300
commit92486c9015e4cecb45c0cd560087b8b9732357d6 (patch)
tree28d39d877162d4470ce9a25107b021d71f52c7fe /coredump/criu_coredump/coredump.py
parent308741d059d7997bf6dc1f25760a602aa4d7898d (diff)
coredump: Rename dir to coredump
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Diffstat (limited to 'coredump/criu_coredump/coredump.py')
-rw-r--r--coredump/criu_coredump/coredump.py829
1 files changed, 829 insertions, 0 deletions
diff --git a/coredump/criu_coredump/coredump.py b/coredump/criu_coredump/coredump.py
new file mode 100644
index 000000000..df055dcb2
--- /dev/null
+++ b/coredump/criu_coredump/coredump.py
@@ -0,0 +1,829 @@
+# Functions and classes for creating core dump from criu images.
+# Code is inspired by outdated google coredumper(RIP) [1] and
+# fs/binfmt_elf.h from Linux kernel [2].
+#
+# [1] https://code.google.com/p/google-coredumper/
+# probably already dead, so consider trying:
+# https://github.com/efiop/google-coredumper/
+# [2] https://www.kernel.org/
+#
+# On my x86_64 systems with fresh kernel ~3.17 core dump looks like:
+#
+# 1) Elf file header;
+# 2) PT_NOTE program header describing notes section;
+# 3) PT_LOAD program headers for (almost?) each vma;
+# 4) NT_PRPSINFO note with elf_prpsinfo inside;
+# 5) An array of notes for each thread of the process:
+# NT_PRSTATUS note with elf_prstatus inside;
+# NT_FPREGSET note with elf_fpregset inside;
+# NT_X86_XSTATE note with x86 extended state using xsave;
+# NT_SIGINFO note with siginfo_t inside;
+# 6) NT_AUXV note with auxv;
+# 7) NT_FILE note with mapped files;
+# 8) VMAs themselves;
+#
+# Or, you can represent it in less details as:
+# 1) Elf file header;
+# 2) Program table;
+# 3) Notes;
+# 4) VMAs contents;
+#
+import io
+import elf
+import ctypes
+from pycriu import images
+
+# Some memory-related constants
+PAGESIZE = 4096
+status = {
+ "VMA_AREA_NONE" : 0 << 0,
+ "VMA_AREA_REGULAR" : 1 << 0,
+ "VMA_AREA_STACK" : 1 << 1,
+ "VMA_AREA_VSYSCALL" : 1 << 2,
+ "VMA_AREA_VDSO" : 1 << 3,
+ "VMA_FORCE_READ" : 1 << 4,
+ "VMA_AREA_HEAP" : 1 << 5,
+ "VMA_FILE_PRIVATE" : 1 << 6,
+ "VMA_FILE_SHARED" : 1 << 7,
+ "VMA_ANON_SHARED" : 1 << 8,
+ "VMA_ANON_PRIVATE" : 1 << 9,
+ "VMA_AREA_SYSVIPC" : 1 << 10,
+ "VMA_AREA_SOCKET" : 1 << 11,
+ "VMA_AREA_VVAR" : 1 << 12,
+ "VMA_AREA_AIORING" : 1 << 13,
+ "VMA_AREA_UNSUPP" : 1 << 31
+}
+
+prot = {
+ "PROT_READ" : 0x1,
+ "PROT_WRITE" : 0x2,
+ "PROT_EXEC" : 0x4
+}
+
+class elf_note:
+ nhdr = None # Elf_Nhdr;
+ owner = None # i.e. CORE or LINUX;
+ data = None # Ctypes structure with note data;
+
+
+class coredump:
+ """
+ A class to keep elf core dump components inside and
+ functions to properly write them to file.
+ """
+ ehdr = None # Elf ehdr;
+ phdrs = [] # Array of Phdrs;
+ notes = [] # Array of elf_notes;
+ vmas = [] # Array of BytesIO with memory content;
+ # FIXME keeping all vmas in memory is a bad idea;
+
+ def write(self, f):
+ """
+ Write core dump to file f.
+ """
+ buf = io.BytesIO()
+ buf.write(self.ehdr)
+
+ for phdr in self.phdrs:
+ buf.write(phdr)
+
+ for note in self.notes:
+ buf.write(note.nhdr)
+ buf.write(note.owner)
+ buf.write("\0"*(8-len(note.owner)))
+ buf.write(note.data)
+
+ offset = ctypes.sizeof(elf.Elf64_Ehdr())
+ offset += (len(self.vmas) + 1)*ctypes.sizeof(elf.Elf64_Phdr())
+
+ filesz = 0
+ for note in self.notes:
+ filesz += ctypes.sizeof(note.nhdr) + ctypes.sizeof(note.data) + 8
+
+ note_align = PAGESIZE - ((offset + filesz) % PAGESIZE)
+
+ if note_align == PAGESIZE:
+ note_align = 0
+
+ if note_align != 0:
+ scratch = (ctypes.c_char * note_align)()
+ ctypes.memset(ctypes.addressof(scratch), 0, ctypes.sizeof(scratch))
+ buf.write(scratch)
+
+ for vma in self.vmas:
+ buf.write(vma.data)
+
+ buf.seek(0)
+ f.write(buf.read())
+
+
+class coredump_generator:
+ """
+ Generate core dump from criu images.
+ """
+ coredumps = {} # coredumps by pid;
+
+ pstree = {} # process info by pid;
+ cores = {} # cores by pid;
+ mms = {} # mm by pid;
+ reg_files = None # reg-files;
+ pagemaps = {} # pagemap by pid;
+
+ def _img_open_and_strip(self, name, single = False, pid = None):
+ """
+ Load criu image and strip it from magic and redundant list.
+ """
+ path = self._imgs_dir + "/" + name
+ if pid:
+ path += "-"+str(pid)
+ path += ".img"
+
+ with open(path) as f:
+ img = images.load(f)
+
+ if single:
+ return img["entries"][0]
+ else:
+ return img["entries"]
+
+
+ def __call__(self, imgs_dir):
+ """
+ Parse criu images stored in directory imgs_dir to fill core dumps.
+ """
+ self._imgs_dir = imgs_dir
+ pstree = self._img_open_and_strip("pstree")
+
+ for p in pstree:
+ pid = p['pid']
+
+ self.pstree[pid] = p
+ for tid in p['threads']:
+ self.cores[tid] = self._img_open_and_strip("core", True, tid)
+ self.mms[pid] = self._img_open_and_strip("mm", True, pid)
+ self.pagemaps[pid] = self._img_open_and_strip("pagemap", False, pid)
+
+ self.reg_files = self._img_open_and_strip("reg-files", False)
+
+ for pid in self.pstree:
+ self.coredumps[pid] = self._gen_coredump(pid)
+
+ return self.coredumps
+
+
+ def write(self, coredumps_dir, pid = None):
+ """
+ Write core dumpt to cores_dir directory. Specify pid to choose
+ core dump of only one process.
+ """
+ for p in self.coredumps:
+ if pid and p != pid:
+ continue
+ with open(coredumps_dir+"/"+"core."+str(p), 'w+') as f:
+ self.coredumps[p].write(f)
+
+ def _gen_coredump(self, pid):
+ """
+ Generate core dump for pid.
+ """
+ cd = coredump()
+
+ # Generate everything backwards so it is easier to calculate offset.
+ cd.vmas = self._gen_vmas(pid)
+ cd.notes = self._gen_notes(pid)
+ cd.phdrs = self._gen_phdrs(pid, cd.notes, cd.vmas)
+ cd.ehdr = self._gen_ehdr(pid, cd.phdrs)
+
+ return cd
+
+ def _gen_ehdr(self, pid, phdrs):
+ """
+ Generate elf header for process pid with program headers phdrs.
+ """
+ ehdr = elf.Elf64_Ehdr()
+
+ ctypes.memset(ctypes.addressof(ehdr), 0, ctypes.sizeof(ehdr))
+ ehdr.e_ident[elf.EI_MAG0] = elf.ELFMAG0
+ ehdr.e_ident[elf.EI_MAG1] = elf.ELFMAG1
+ ehdr.e_ident[elf.EI_MAG2] = elf.ELFMAG2
+ ehdr.e_ident[elf.EI_MAG3] = elf.ELFMAG3
+ ehdr.e_ident[elf.EI_CLASS] = elf.ELFCLASS64
+ ehdr.e_ident[elf.EI_DATA] = elf.ELFDATA2LSB
+ ehdr.e_ident[elf.EI_VERSION] = elf.EV_CURRENT
+
+ ehdr.e_type = elf.ET_CORE
+ ehdr.e_machine = elf.EM_X86_64
+ ehdr.e_version = elf.EV_CURRENT
+ ehdr.e_phoff = ctypes.sizeof(elf.Elf64_Ehdr())
+ ehdr.e_ehsize = ctypes.sizeof(elf.Elf64_Ehdr())
+ ehdr.e_phentsize = ctypes.sizeof(elf.Elf64_Phdr())
+ #FIXME Case len(phdrs) > PN_XNUM should be handled properly.
+ # See fs/binfmt_elf.c from linux kernel.
+ ehdr.e_phnum = len(phdrs)
+
+ return ehdr
+
+ def _gen_phdrs(self, pid, notes, vmas):
+ """
+ Generate program headers for process pid.
+ """
+ phdrs = []
+
+ offset = ctypes.sizeof(elf.Elf64_Ehdr())
+ offset += (len(vmas) + 1)*ctypes.sizeof(elf.Elf64_Phdr())
+
+ filesz = 0
+ for note in notes:
+ filesz += ctypes.sizeof(note.nhdr) + ctypes.sizeof(note.data) + 8
+
+ # PT_NOTE
+ phdr = elf.Elf64_Phdr()
+ ctypes.memset(ctypes.addressof(phdr), 0, ctypes.sizeof(phdr))
+ phdr.p_type = elf.PT_NOTE
+ phdr.p_offset = offset
+ phdr.p_filesz = filesz
+
+ phdrs.append(phdr)
+
+ note_align = PAGESIZE - ((offset + filesz) % PAGESIZE)
+
+ if note_align == PAGESIZE:
+ note_align = 0
+
+ offset += note_align
+
+ # VMA phdrs
+
+ for vma in vmas:
+ offset += filesz
+ filesz = vma.filesz
+ phdr = elf.Elf64_Phdr()
+ ctypes.memset(ctypes.addressof(phdr), 0, ctypes.sizeof(phdr))
+ phdr.p_type = elf.PT_LOAD
+ phdr.p_align = PAGESIZE
+ phdr.p_paddr = 0
+ phdr.p_offset = offset
+ phdr.p_vaddr = vma.start
+ phdr.p_memsz = vma.memsz
+ phdr.p_filesz = vma.filesz
+ phdr.p_flags = vma.flags
+
+ phdrs.append(phdr)
+
+ return phdrs
+
+ def _gen_prpsinfo(self, pid):
+ """
+ Generate NT_PRPSINFO note for process pid.
+ """
+ pstree = self.pstree[pid]
+ core = self.cores[pid]
+
+ prpsinfo = elf.elf_prpsinfo()
+ ctypes.memset(ctypes.addressof(prpsinfo), 0, ctypes.sizeof(prpsinfo))
+
+ # FIXME TASK_ALIVE means that it is either running or sleeping, need to
+ # teach criu to distinguish them.
+ TASK_ALIVE = 0x1
+ # XXX A bit of confusion here, as in ps "dead" and "zombie"
+ # state are two separate states, and we use TASK_DEAD for zombies.
+ TASK_DEAD = 0x2
+ TASK_STOPPED = 0x3
+ if core["tc"]["task_state"] == TASK_ALIVE:
+ prpsinfo.pr_state = 0
+ if core["tc"]["task_state"] == TASK_DEAD:
+ prpsinfo.pr_state = 4
+ if core["tc"]["task_state"] == TASK_STOPPED:
+ prpsinfo.pr_state = 3
+ # Don't even ask me why it is so, just borrowed from linux
+ # source and made pr_state match.
+ prpsinfo.pr_sname = '.' if prpsinfo.pr_state > 5 else "RSDTZW"[prpsinfo.pr_state]
+ prpsinfo.pr_zomb = 1 if prpsinfo.pr_state == 4 else 0
+ prpsinfo.pr_nice = core["thread_core"]["sched_prio"] if "sched_prio" in core["thread_core"] else 0
+ prpsinfo.pr_flag = core["tc"]["flags"]
+ prpsinfo.pr_uid = core["thread_core"]["creds"]["uid"]
+ prpsinfo.pr_gid = core["thread_core"]["creds"]["gid"]
+ prpsinfo.pr_pid = pid
+ prpsinfo.pr_ppid = pstree["ppid"]
+ prpsinfo.pr_pgrp = pstree["pgid"]
+ prpsinfo.pr_sid = pstree["sid"]
+ prpsinfo.pr_fname = core["tc"]["comm"]
+ prpsinfo.pr_psargs = self._gen_cmdline(pid)
+
+ nhdr = elf.Elf64_Nhdr()
+ nhdr.n_namesz = 5
+ nhdr.n_descsz = ctypes.sizeof(elf.elf_prpsinfo())
+ nhdr.n_type = elf.NT_PRPSINFO
+
+ note = elf_note()
+ note.data = prpsinfo
+ note.owner = "CORE"
+ note.nhdr = nhdr
+
+ return note
+
+ def _gen_prstatus(self, pid, tid):
+ """
+ Generate NT_PRSTATUS note for thread tid of process pid.
+ """
+ core = self.cores[tid]
+ regs = core["thread_info"]["gpregs"]
+ pstree = self.pstree[pid]
+
+ prstatus = elf.elf_prstatus()
+
+ ctypes.memset(ctypes.addressof(prstatus), 0, ctypes.sizeof(prstatus))
+
+ #FIXME setting only some of the fields for now. Revisit later.
+ prstatus.pr_pid = tid
+ prstatus.pr_ppid = pstree["ppid"]
+ prstatus.pr_pgrp = pstree["pgid"]
+ prstatus.pr_sid = pstree["sid"]
+
+ prstatus.pr_reg.r15 = regs["r15"]
+ prstatus.pr_reg.r14 = regs["r14"]
+ prstatus.pr_reg.r13 = regs["r13"]
+ prstatus.pr_reg.r12 = regs["r12"]
+ prstatus.pr_reg.rbp = regs["bp"]
+ prstatus.pr_reg.rbx = regs["bx"]
+ prstatus.pr_reg.r11 = regs["r11"]
+ prstatus.pr_reg.r10 = regs["r10"]
+ prstatus.pr_reg.r9 = regs["r9"]
+ prstatus.pr_reg.r8 = regs["r8"]
+ prstatus.pr_reg.rax = regs["ax"]
+ prstatus.pr_reg.rcx = regs["cx"]
+ prstatus.pr_reg.rdx = regs["dx"]
+ prstatus.pr_reg.rsi = regs["si"]
+ prstatus.pr_reg.rdi = regs["di"]
+ prstatus.pr_reg.orig_rax = regs["orig_ax"]
+ prstatus.pr_reg.rip = regs["ip"]
+ prstatus.pr_reg.cs = regs["cs"]
+ prstatus.pr_reg.eflags = regs["flags"]
+ prstatus.pr_reg.rsp = regs["sp"]
+ prstatus.pr_reg.ss = regs["ss"]
+ prstatus.pr_reg.fs_base = regs["fs_base"]
+ prstatus.pr_reg.gs_base = regs["gs_base"]
+ prstatus.pr_reg.ds = regs["ds"]
+ prstatus.pr_reg.es = regs["es"]
+ prstatus.pr_reg.fs = regs["fs"]
+ prstatus.pr_reg.gs = regs["gs"]
+
+ nhdr = elf.Elf64_Nhdr()
+ nhdr.n_namesz = 5
+ nhdr.n_descsz = ctypes.sizeof(elf.elf_prstatus())
+ nhdr.n_type = elf.NT_PRSTATUS
+
+ note = elf_note()
+ note.data = prstatus
+ note.owner = "CORE"
+ note.nhdr = nhdr
+
+ return note
+
+ def _gen_fpregset(self, pid, tid):
+ """
+ Generate NT_FPREGSET note for thread tid of process pid.
+ """
+ core = self.cores[tid]
+ regs = core["thread_info"]["fpregs"]
+
+ fpregset = elf.elf_fpregset_t()
+ ctypes.memset(ctypes.addressof(fpregset), 0, ctypes.sizeof(fpregset))
+
+ fpregset.cwd = regs["cwd"]
+ fpregset.swd = regs["swd"]
+ fpregset.ftw = regs["twd"]
+ fpregset.fop = regs["fop"]
+ fpregset.rip = regs["rip"]
+ fpregset.rdp = regs["rdp"]
+ fpregset.mxcsr = regs["mxcsr"]
+ fpregset.mxcr_mask = regs["mxcsr_mask"]
+ fpregset.st_space = (ctypes.c_uint * len(regs["st_space"]))(*regs["st_space"])
+ fpregset.xmm_space = (ctypes.c_uint * len(regs["xmm_space"]))(*regs["xmm_space"])
+ #fpregset.padding = regs["padding"] unused
+
+ nhdr = elf.Elf64_Nhdr()
+ nhdr.n_namesz = 5
+ nhdr.n_descsz = ctypes.sizeof(elf.elf_fpregset_t())
+ nhdr.n_type = elf.NT_FPREGSET
+
+ note = elf_note()
+ note.data = fpregset
+ note.owner = "CORE"
+ note.nhdr = nhdr
+
+ return note
+
+ def _gen_x86_xstate(self, pid, tid):
+ """
+ Generate NT_X86_XSTATE note for thread tid of process pid.
+ """
+ core = self.cores[tid]
+ fpregs = core["thread_info"]["fpregs"]
+
+ data = elf.elf_xsave_struct()
+ ctypes.memset(ctypes.addressof(data), 0, ctypes.sizeof(data))
+
+ data.i387.cwd = fpregs["cwd"]
+ data.i387.swd = fpregs["swd"]
+ data.i387.twd = fpregs["twd"]
+ data.i387.fop = fpregs["fop"]
+ data.i387.rip = fpregs["rip"]
+ data.i387.rdp = fpregs["rdp"]
+ data.i387.mxcsr = fpregs["mxcsr"]
+ data.i387.mxcsr_mask = fpregs["mxcsr_mask"]
+ data.i387.st_space = (ctypes.c_uint * len(fpregs["st_space"]))(*fpregs["st_space"])
+ data.i387.xmm_space = (ctypes.c_uint * len(fpregs["xmm_space"]))(*fpregs["xmm_space"])
+
+ if "xsave" in fpregs:
+ data.xsave_hdr.xstate_bv = fpregs["xsave"]["xstate_bv"]
+ data.ymmh.ymmh_space = (ctypes.c_uint * len(fpregs["xsave"]["ymmh_space"]))(*fpregs["xsave"]["ymmh_space"])
+
+ nhdr = elf.Elf64_Nhdr()
+ nhdr.n_namesz = 6
+ nhdr.n_descsz = ctypes.sizeof(data)
+ nhdr.n_type = elf.NT_X86_XSTATE
+
+ note = elf_note()
+ note.data = data
+ note.owner = "LINUX"
+ note.nhdr = nhdr
+
+ return note
+
+ def _gen_siginfo(self, pid, tid):
+ """
+ Generate NT_SIGINFO note for thread tid of process pid.
+ """
+ siginfo = elf.siginfo_t()
+ # FIXME zeroify everything for now
+ ctypes.memset(ctypes.addressof(siginfo), 0, ctypes.sizeof(siginfo))
+
+ nhdr = elf.Elf64_Nhdr()
+ nhdr.n_namesz = 5
+ nhdr.n_descsz = ctypes.sizeof(elf.siginfo_t())
+ nhdr.n_type = elf.NT_SIGINFO
+
+ note = elf_note()
+ note.data = siginfo
+ note.owner = "CORE"
+ note.nhdr = nhdr
+
+ return note
+
+ def _gen_auxv(self, pid):
+ """
+ Generate NT_AUXV note for thread tid of process pid.
+ """
+ mm = self.mms[pid]
+ num_auxv = len(mm["mm_saved_auxv"])/2
+
+ class elf_auxv(ctypes.Structure):
+ _fields_ = [("auxv", elf.Elf64_auxv_t*num_auxv)]
+
+ auxv = elf_auxv()
+ for i in range(num_auxv):
+ auxv.auxv[i].a_type = mm["mm_saved_auxv"][i]
+ auxv.auxv[i].a_val = mm["mm_saved_auxv"][i+1]
+
+ nhdr = elf.Elf64_Nhdr()
+ nhdr.n_namesz = 5
+ nhdr.n_descsz = ctypes.sizeof(elf_auxv())
+ nhdr.n_type = elf.NT_AUXV
+
+ note = elf_note()
+ note.data = auxv
+ note.owner = "CORE"
+ note.nhdr = nhdr
+
+ return note
+
+ def _gen_files(self, pid):
+ """
+ Generate NT_FILE note for process pid.
+ """
+ mm = self.mms[pid]
+
+ class mmaped_file_info:
+ start = None
+ end = None
+ file_ofs = None
+ name = None
+
+ infos = []
+ for vma in mm["vmas"]:
+ if vma["shmid"] == 0:
+ # shmid == 0 means that it is not a file
+ continue
+
+ shmid = vma["shmid"]
+ size = vma["end"] - vma["start"]
+ off = vma["pgoff"]/PAGESIZE
+
+ files = self.reg_files
+ fname = filter(lambda x: x["id"] == shmid, files)[0]["name"]
+
+ info = mmaped_file_info()
+ info.start = vma["start"]
+ info.end = vma["end"]
+ info.file_ofs = off
+ info.name = fname
+
+ infos.append(info)
+
+ # /*
+ # * Format of NT_FILE note:
+ # *
+ # * long count -- how many files are mapped
+ # * long page_size -- units for file_ofs
+ # * array of [COUNT] elements of
+ # * long start
+ # * long end
+ # * long file_ofs
+ # * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
+ # */
+ fields = []
+ fields.append(("count", ctypes.c_long))
+ fields.append(("page_size", ctypes.c_long))
+ for i in range(len(infos)):
+ fields.append(("start"+str(i), ctypes.c_long))
+ fields.append(("end"+str(i), ctypes.c_long))
+ fields.append(("file_ofs"+str(i), ctypes.c_long))
+ for i in range(len(infos)):
+ fields.append(("name"+str(i), ctypes.c_char*(len(infos[i].name)+1)))
+
+ class elf_files(ctypes.Structure):
+ _fields_ = fields
+
+ data = elf_files()
+ data.count = len(infos)
+ data.page_size = PAGESIZE
+ for i in range(len(infos)):
+ info = infos[i]
+ setattr(data, "start"+str(i), info.start)
+ setattr(data, "end"+str(i), info.end)
+ setattr(data, "file_ofs"+str(i), info.file_ofs)
+ setattr(data, "name"+str(i), info.name)
+
+ nhdr = elf.Elf64_Nhdr()
+
+ nhdr.n_namesz = 5#XXX strlen + 1
+ nhdr.n_descsz = ctypes.sizeof(elf_files())
+ nhdr.n_type = elf.NT_FILE
+
+ note = elf_note()
+ note.nhdr = nhdr
+ note.owner = "CORE"
+ note.data = data
+
+ return note
+
+ def _gen_thread_notes(self, pid, tid):
+ notes = []
+
+ notes.append(self._gen_prstatus(pid, tid))
+ notes.append(self._gen_fpregset(pid, tid))
+ notes.append(self._gen_x86_xstate(pid, tid))
+ notes.append(self._gen_siginfo(pid, tid))
+
+ return notes
+
+ def _gen_notes(self, pid):
+ """
+ Generate notes for core dump of process pid.
+ """
+ notes = []
+
+ notes.append(self._gen_prpsinfo(pid))
+
+ threads = self.pstree[pid]["threads"]
+
+ # Main thread first
+ notes += self._gen_thread_notes(pid, pid)
+
+ # Then other threads
+ for tid in threads:
+ if tid == pid:
+ continue
+
+ notes += self._gen_thread_notes(pid, tid)
+
+ notes.append(self._gen_auxv(pid))
+ notes.append(self._gen_files(pid))
+
+ return notes
+
+ def _get_page(self, pid, page_no):
+ """
+ Try to find memory page page_no in pages.img image for process pid.
+ """
+ pagemap = self.pagemaps[pid]
+
+ # First entry is pagemap_head, we will need it later to open
+ # proper pages.img.
+ pages_id = pagemap[0]["pages_id"]
+ off = 0# in pages
+ for m in pagemap[1:]:
+ found = False
+ for i in xrange(m["nr_pages"]):
+ if m["vaddr"] + i*PAGESIZE == page_no*PAGESIZE:
+ found = True
+ break
+ off += 1
+
+ if not found:
+ continue
+
+ if "in_parent" in m and m["in_parent"] == True:
+ ppid = self.pstree[pid]["ppid"]
+ return self._get_page(ppid, page_no)
+ else:
+ with open(self._imgs_dir+"/"+"pages-"+str(pages_id)+".img") as f:
+ f.seek(off*PAGESIZE)
+ return f.read(PAGESIZE)
+
+ return None
+
+ def _gen_mem_chunk(self, pid, vma, size):
+ """
+ Obtain vma contents for process pid.
+ """
+ f = None
+
+ if size == 0:
+ return ""
+
+ if vma["status"] & status["VMA_AREA_VVAR"]:
+ #FIXME this is what gdb does, as vvar vma
+ # is not readable from userspace?
+ return "\0"*size
+ elif vma["status"] & status["VMA_AREA_VSYSCALL"]:
+ #FIXME need to dump it with criu or read from
+ # current process.
+ return "\0"*size
+
+ if vma["status"] & status["VMA_FILE_SHARED"] or \
+ vma["status"] & status["VMA_FILE_PRIVATE"]:
+ # Open file before iterating vma pages
+ shmid = vma["shmid"]
+ off = vma["pgoff"]
+
+ files = self.reg_files
+ fname = filter(lambda x: x["id"] == shmid, files)[0]["name"]
+
+ f = open(fname)
+ f.seek(off)
+
+ start = vma["start"]
+ end = vma["start"] + size
+
+ # Split requested memory chunk into pages, so it could be
+ # pictured as:
+ #
+ # "----" -- part of page with memory outside of our vma;
+ # "XXXX" -- memory from our vma;
+ #
+ # Start page Pages in the middle End page
+ # [-----XXXXX]...[XXXXXXXXXX][XXXXXXXXXX]...[XXX-------]
+ #
+ # Each page could be found in pages.img or in a standalone
+ # file described by shmid field in vma entry and
+ # corresponding entry in reg-files.img.
+ # For VMA_FILE_PRIVATE vma, unchanged pages are taken from
+ # a file, and changed ones -- from pages.img.
+ # Finally, if no page is found neither in pages.img nor
+ # in file, hole in inserted -- a page filled with zeroes.
+ start_page = start/PAGESIZE
+ end_page = end/PAGESIZE
+
+ buf = ""
+ for page_no in range(start_page, end_page+1):
+ page = None
+
+ # Search for needed page in pages.img and reg-files.img
+ # and choose appropriate.
+ page_mem = self._get_page(pid, page_no)
+
+ if f != None:
+ page = f.read(PAGESIZE)
+
+ if page_mem != None:
+ # Page from pages.img has higher priority
+ # than one from maped file on disk.
+ page = page_mem
+
+ if page == None:
+ # Hole
+ page = PAGESIZE*"\0"
+
+ # If it is a start or end page, we need to read
+ # only part of it.
+ if page_no == start_page:
+ n_skip = start - page_no*PAGESIZE
+ if start_page == end_page:
+ n_read = size
+ else:
+ n_read = PAGESIZE - n_skip
+ elif page_no == end_page:
+ n_skip = 0
+ n_read = end - page_no*PAGESIZE
+ else:
+ n_skip = 0
+ n_read = PAGESIZE
+
+ buf += page[n_skip : n_skip + n_read]
+
+ # Don't forget to close file.
+ if f != None:
+ f.close()
+
+ return buf
+
+ def _gen_cmdline(self, pid):
+ """
+ Generate full command with arguments.
+ """
+ mm = self.mms[pid]
+
+ vma = {}
+ vma["start"] = mm["mm_arg_start"]
+ vma["end"] = mm["mm_arg_end"]
+ # Dummy flags and status.
+ vma["flags"] = 0
+ vma["status"] = 0
+ size = vma["end"] - vma["start"]
+
+ chunk = self._gen_mem_chunk(pid, vma, size)
+
+ # Replace all '\0's with spaces.
+ return chunk.replace('\0', ' ')
+
+ def _get_vma_dump_size(self, vma):
+ """
+ Calculate amount of vma to put into core dump.
+ """
+ if vma["status"] & status["VMA_AREA_VVAR"] or \
+ vma["status"] & status["VMA_AREA_VSYSCALL"] or \
+ vma["status"] & status["VMA_AREA_VDSO"]:
+ size = vma["end"] - vma["start"]
+ elif vma["prot"] == 0:
+ size = 0
+ elif vma["prot"] & prot["PROT_READ"] and \
+ vma["prot"] & prot["PROT_EXEC"]:
+ size = PAGESIZE
+ elif vma["status"] & status["VMA_ANON_SHARED"] or \
+ vma["status"] & status["VMA_FILE_SHARED"] or \
+ vma["status"] & status["VMA_ANON_PRIVATE"] or \
+ vma["status"] & status["VMA_FILE_PRIVATE"]:
+ size = vma["end"] - vma["start"]
+ else:
+ size = 0
+
+ return size
+
+ def _get_vma_flags(self, vma):
+ """
+ Convert vma flags int elf flags.
+ """
+ flags = 0
+
+ if vma['prot'] & prot["PROT_READ"]:
+ flags = flags | elf.PF_R
+
+ if vma['prot'] & prot["PROT_WRITE"]:
+ flags = flags | elf.PF_W
+
+ if vma['prot'] & prot["PROT_EXEC"]:
+ flags = flags | elf.PF_X
+
+ return flags
+
+ def _gen_vmas(self, pid):
+ """
+ Generate vma contents for core dump for process pid.
+ """
+ mm = self.mms[pid]
+
+ class vma_class:
+ data = None
+ filesz = None
+ memsz = None
+ flags = None
+ start = None
+
+ vmas = []
+ for vma in mm["vmas"]:
+ size = self._get_vma_dump_size(vma)
+
+ chunk = self._gen_mem_chunk(pid, vma, size)
+
+ v = vma_class()
+ v.filesz = self._get_vma_dump_size(vma)
+ v.data = self._gen_mem_chunk(pid, vma, v.filesz)
+ v.memsz = vma["end"] - vma["start"]
+ v.start = vma["start"]
+ v.flags = self._get_vma_flags(vma)
+
+ vmas.append(v)
+
+ return vmas