Skip to content

Instantly share code, notes, and snippets.

@emperorcezar
Created November 15, 2016 23:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emperorcezar/8565ba88e614423feb422efc085ae3d3 to your computer and use it in GitHub Desktop.
Save emperorcezar/8565ba88e614423feb422efc085ae3d3 to your computer and use it in GitHub Desktop.
# This file is part of pyrasite.
#
# pyrasite 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.
#
# pyrasite 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 pyrasite. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2011-2013 Red Hat, Inc.
# Authors: Luke Macken <lmacken@redhat.com>
"""
An interface for visualizing the output of of the dump-memory payload.
When selecting an object, we attach to the running process and obtain
the value of the object itself.
"""
__version__ = '1.0'
import os
import re
import sys
import urwid
import urwid.raw_display
from meliae import loader
from os.path import join, abspath, dirname
import pyrasite
class PyrasiteMemoryViewer(object):
palette = [
('body', 'black', 'light gray', 'standout'),
('header', 'white', 'dark red', 'bold'),
('button normal', 'light gray', 'dark blue', 'standout'),
('button select', 'white', 'dark green'),
('button disabled', 'dark gray', 'dark blue'),
('bigtext', 'white', 'black'),
('object_output', 'light gray', 'black'),
('exit', 'white', 'dark red'),
]
def __init__(self, pid, objects):
self.pid = pid
self.objects = objects
self.summary = objects.summarize()
def create_radio_button(self, g, name, obj, fn, disabled=False):
w = urwid.RadioButton(g, name, False, on_state_change=fn)
w.obj = obj
if disabled:
w = urwid.AttrWrap(w, 'button normal', 'button disabled')
else:
w = urwid.AttrWrap(w, 'button normal', 'button select')
return w
def create_disabled_radio_button(self, name):
w = urwid.Text(' ' + name)
w = urwid.AttrWrap(w, 'button disabled')
return w
def display_object(self, w, state):
if state:
value = pyrasite.inspect(self.pid, w.obj.max_address)
if not value:
value = 'Unable to inspect remote object. Make sure you have ' \
'the python-debuginfo package installed.'
self.object_output.set_text(value)
def get_object_buttons(self, group=[]):
buttons = []
for i, line in enumerate(str(self.summary).split('\n')):
if i in (0, 1):
rb = self.create_disabled_radio_button(line)
else:
obj = self.summary.summaries[i - 2]
rb = self.create_radio_button(group, line, obj,
self.display_object)
buttons.append(rb)
return buttons
def setup_view(self):
self.object_buttons = self.get_object_buttons()
# Title
self.bigtext = urwid.BigText('pyrasite ' + __version__, None)
self.bigtext.set_font(urwid.Thin6x6Font())
bt = urwid.Padding(self.bigtext, 'left', None)
bt = urwid.AttrWrap(bt, 'bigtext')
# Create the object outpu
self.object_output = urwid.Text("", wrap='any')
ca = urwid.AttrWrap(self.object_output, 'object_output')
# Select the first object
self.object_buttons[2].set_state(True)
# ListBox
obj_out = urwid.Pile([urwid.Divider(), ca])
objects = urwid.Pile(self.object_buttons)
l = [objects, obj_out]
w = urwid.ListBox(urwid.SimpleListWalker(l))
# Frame
w = urwid.AttrWrap(w, 'body')
w = urwid.Frame(header=bt, body=w)
# Exit message
exit = urwid.BigText(('exit', " Quit? "), urwid.Thin6x6Font())
exit = urwid.Overlay(exit, w, 'center', None, 'middle', None)
return w, exit
def unhandled_input(self, key):
if key in ('f8', 'q'):
self.loop.widget = self.exit_view
return True
if self.loop.widget != self.exit_view:
return
if key in ('y', 'Y'):
raise urwid.ExitMainLoop()
if key in ('n', 'N'):
self.loop.widget = self.view
return True
def main(self):
self.view, self.exit_view = self.setup_view()
self.loop = urwid.MainLoop(self.view, self.palette,
unhandled_input=self.unhandled_input)
self.loop.run()
def main():
if len(sys.argv) != 2:
print("[ pyrasite memory viewer ]\n")
print("Usage: %s <pid> <objects.json>" % sys.argv[0])
print("\n pid - the running process id")
print("")
sys.exit(1)
filename = sys.argv[1]
pid = 1
# Work around bug caused by meliae dumping unicode strings:
# https://bugs.launchpad.net/meliae/+bug/876810
with open(filename) as sample_file, open(filename + '.tmp', 'w') as output_file:
pattern = re.compile(r"(?<!\\)\\u([dD][0-9a-fA-F]{3,3})")
for line in sample_file:
output_file.write(pattern.sub("#S\g<1>", line))
os.rename(filename + '.tmp', filename)
objects = loader.load(filename)
objects.compute_referrers()
PyrasiteMemoryViewer(pid=pid, objects=objects).main()
if '__main__' == __name__:
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment