Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 18 additions & 16 deletions Decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
import os
import sys
import time
from datetime import datetime
import threading
from tempfile import gettempdir
import time
import heapq
import pickle
import io
import cProfile, pstats, io

if builtins.__dict__['GUI_FLAG']:
import wx
Expand Down Expand Up @@ -39,29 +40,30 @@ def __call__(self, *args):
self.memoized[args] = self.function(*args)
return self.memoized[args]

hotshotProfilers = {}
def hotshotit(func):
def wrapper(*args, **kw):
sim_thread = args[0]
prof = sim_thread.prof
### if profiling check-box is checked in the simulationDialog
if prof:

### name of .prof file
label = sim_thread.model.getBlockModel().label
now = datetime.now() # current date and time
date_time = now.strftime('%m-%d-%Y_%H-%M-%S')
prof_name = os.path.join(gettempdir(),"%s_%s_%s%s"%(func.__name__, label, date_time ,'.prof'))

### profiling section with cProfile
pr = cProfile.Profile()
pr.enable()
r = func(*args, **kw)
pr.disable()
#Sort the statistics by the cumulative time spent in the function
sortby = 'cumulative'
ps = pstats.Stats(pr).sort_stats(sortby)
ps.dump_stats(prof_name)
#print(s.getvalue())

try:
import hotshot
#import cProfile as hotshot
except ImportError:
sys.stderr.write(_("Please install hotshot module."))
return

global hotshotProfilers
prof_name = os.path.join(gettempdir(),"%s_%s%s"%(func.__name__, label, '.prof'))
profiler = hotshotProfilers.get(prof_name)
if profiler is None:
profiler = hotshot.Profile(prof_name)
hotshotProfilers[prof_name] = profiler
r = profiler.runcall(func, *args, **kw)
else:
r = func(*args, **kw)
return r
Expand Down
38 changes: 27 additions & 11 deletions Menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import os
import sys
import platform
import profile

from tempfile import gettempdir

Expand Down Expand Up @@ -251,8 +252,10 @@ def __init__(self, parent):
parent.Bind(wx.EVT_MENU, parent.OnProfiling, id=id)

self.AppendSeparator()

AppendItem(wx.MenuItem(self, ID_DELETE_PROFILES, _("Delete all")))
self.Enable(ID_DELETE_PROFILES, self.GetMenuItemCount() > 2)

parent.Bind(wx.EVT_MENU, parent.OnDeleteProfiles, id = ID_DELETE_PROFILES)

class RecentFileMenu(wx.Menu):
Expand Down Expand Up @@ -449,10 +452,8 @@ def __init__(self, parent):
AppendMenu(self, wx.NewIdRef(), _('Languages'), languagesSubmenu)

### Before Phoenix transition
ishotshot = 'hotshot' in list(sys.modules.keys())
if ishotshot:
AppendMenu(self, ID_PROFILE, _('Profile'), ProfileFileMenu(parent))

AppendMenu(self, ID_PROFILE, _('Profile'), ProfileFileMenu(parent))

parent = parent.GetParent()

AppendItem(pref_item)
Expand All @@ -463,7 +464,7 @@ def __init__(self, parent):
parent.Bind(wx.EVT_MENU, parent.OnFrench, id=ID_FRENCH_LANGUAGE)
parent.Bind(wx.EVT_MENU, parent.OnEnglish, id=ID_ENGLISH_LANGUAGE)
parent.Bind(wx.EVT_MENU, parent.OnAdvancedSettings, id=ID_PREFERENCES)

class HelpMenu(wx.Menu):
"""
"""
Expand Down Expand Up @@ -509,7 +510,7 @@ def __init__(self, parent):
self.Append(SettingsMenu(self), _("&Options"))
self.Append(HelpMenu(self), _("&Help"))

self.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight)
self.Bind(wx.EVT_MENU_HIGHLIGHT, self.OnMenuHighlight)

### useless until Phoenix transition
def OnOpenMenu(self, event):
Expand All @@ -520,6 +521,8 @@ def OnOpenMenu(self, event):

menu = event.GetMenu()

posm = self.FindMenu(menu.GetTitle())

### if the opened menu is the File menu
if isinstance(menu, FileMenu):

Expand All @@ -533,9 +536,15 @@ def OnOpenMenu(self, event):
else:
if platform.system() == 'Windows':
### After Pnoenix Transition
self.Replace(0, FileMenu(self), _("&File"))
self.Replace(posm, FileMenu(self), _("&File"))
else:
label = _("Recent files")
ID = menu.FindItem(label)
item, pos = menu.FindChildItem(ID)
menu.Remove(ID)
menu.Insert(pos, ID, label, RecentFileMenu(self))

elif isinstance(menu, SettingsMenu) and 'hotshot' in list(sys.modules.keys()):
elif isinstance(menu, SettingsMenu):

if wx.VERSION_STRING < '4.0':
### Before Pnoenix Transition
Expand All @@ -545,10 +554,17 @@ def OnOpenMenu(self, event):
### we insert the profile files menu
menu.InsertMenu(1, ID_PROFILE, _('Profile'), ProfileFileMenu(self))
else:
### After Pnoenix Transition
if platform.system() == 'Windows':
### After Pnoenix Transition
self.Replace(4, SettingsMenu(self), _("&Options"))

self.Replace(posm, SettingsMenu(self), _("&Options"))
else:
label = _('Profile')
ID = menu.FindItem(label)
item, pos = menu.FindChildItem(ID)
menu.Remove(ID)
menu.Insert(pos, ID, label, ProfileFileMenu(self))


#def OnCloseMenu(self, event):
#""" Close menu has been detected
#"""
Expand Down
7 changes: 1 addition & 6 deletions SimulationGUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,6 @@ def MakePaneContent(self, pane):
cb5 = wx.CheckBox(pane, wx.NewIdRef(), name='real_time')

if DEFAULT_DEVS_DIRNAME == 'PyDEVS':
if not 'hotshot' in list(sys.modules.keys()):
text3.Enable(False)
cb1.Enable(False)
self.parent.prof = False

self.cb2.SetValue(builtins.__dict__['NTL'])
self.cb3.Enable(False)
cb4.Enable(False)
Expand Down Expand Up @@ -327,7 +322,7 @@ def __init__(self, parent, id, title, master):
### PyPDEVS threaded real time simulation
self.real_time_flag = builtins.__dict__['REAL_TIME']

### profiling simulation with hotshot
### profiling simulation
self.prof = False

### No time limit simulation (defined in the builtin dictionary from .devsimpy file)
Expand Down
2 changes: 1 addition & 1 deletion SimulationNoGUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def __init__(self, master, time):
self.dynamic_structure_flag = builtins.__dict__['DYNAMIC_STRUCTURE']
self.real_time_flag = builtins.__dict__['REAL_TIME']

### profiling simulation with hotshot
### profiling simulation
self.prof = False

self.verbose = False
Expand Down
22 changes: 22 additions & 0 deletions Utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
sys.stdout.write("Unknown operating system.\n")
sys.exit()

import pip
import importlib

#-------------------------------------------------------------------------------
def PrintException():
exc_type, exc_obj, tb = sys.exc_info()
Expand Down Expand Up @@ -123,6 +126,25 @@ def PyBuzyInfo(msg, time):

del busy

def install_and_import(package):
try:
importlib.import_module(package)
installed = True
except:
dial = wx.MessageDialog(None, _('Do you want to install the %s using pip?'%package), _('Install Package'), wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)

if dial.ShowModal() == wx.ID_YES:
pip.main(['install', package])
installed = True
dial.Destroy()
else:
installed = False
dial.Destroy()
finally:
if installed : globals()[package] = importlib.import_module(package)

return installed

def getObjectFromString(scriptlet):
"""
"""
Expand Down
66 changes: 17 additions & 49 deletions devsimpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import shutil
import builtins
import glob
import pstats

from configparser import ConfigParser
from tempfile import gettempdir
Expand Down Expand Up @@ -184,7 +185,7 @@
from PreferencesGUI import PreferencesGUI
from pluginmanager import load_plugins, enable_plugin
from which import which
from Utilities import GetUserConfigDir
from Utilities import GetUserConfigDir, install_and_import
from Decorators import redirectStdout, BuzyCursorNotification
from DetachedFrame import DetachedFrame
from LibraryTree import LibraryTree
Expand Down Expand Up @@ -1888,54 +1889,29 @@ def OnAdvancedSettings(self, event):
###
@BuzyCursorNotification
def OnProfiling(self, event):
""" Simulation profiling for fn file
""" Simulation profiling for fn file.
"""

### find the prof file name
menu_item = self.GetMenuBar().FindItemById(event.GetId())
fn = menu_item.GetLabel()
fn = menu_item.GetItemLabelText()
prof_file_path = os.path.join(gettempdir(), fn)

### list of item in single choice dialogue
choices = [_('Embedded in DEVSimPy')]

### editor of profiling software
try:
kcachegrind = which('kcachegrind')
choices.append('kcachegrind')
except Exception:
kcachegrind = False
try:
kprof = which('kprof')
choices.append('kprof')
except Exception:
kprof = False
try:
converter = which('hotshot2calltree')
except Exception:
converter = False

choices.append(_('Other...'))
choices = ['snakeviz','gprof2dot',_('Embedded in DEVSimPy')]

dlg = wx.SingleChoiceDialog(self, _('What profiling software are you using?'), _('Single Choice'), choices)
if dlg.ShowModal() == wx.ID_OK:
response = dlg.GetStringSelection()
if response == 'kcachegrind':
if response == 'snakeviz':
dlg.Destroy()

if converter:
### cache grind file name that will be generated
cachegrind_fn = os.path.join(gettempdir(), "%s%s"%(fn[:-len('.prof')],'.cachegrind'))
### transform profile file for cachegrind
os.system("%s %s %s %s"%(converter,"-o", cachegrind_fn, prof_file_path))

self.LoadCachegrindFile(cachegrind_fn)
else:
wx.MessageBox(_("Hotshot converter (hotshot2calltree) not found"), _('Error'), wx.OK|wx.ICON_ERROR)

elif response == 'kprof':
r = install_and_import(response)
if r: os.system(" ".join([response,prof_file_path,"&"]))
elif response == 'gprof2dot':
dlg.Destroy()
self.LoadProfFileFromKProf(prof_file_path)
r = install_and_import(response)
png_file_path = prof_file_path.replace('.prof', '.png')
os.system(" ".join([response,'-f pstats',prof_file_path,"|", "dot", "-Tpng", "-o", png_file_path, "&&", "eog", png_file_path]))
elif response == _('Embedded in DEVSimPy'):
dlg.Destroy()
output = self.LoadProfFile(prof_file_path)
Expand All @@ -1945,24 +1921,16 @@ def OnProfiling(self, event):
else:
dlg.Destroy()

@staticmethod
def LoadCachegrindFile(cachegrind_fn):
### lauch kcachegrid
os.system(" ".join(['kcachegrind',cachegrind_fn,"&"]))

@staticmethod
def LoadProfFileFromKProf(prof_file_path):
### lauch kprof
os.system(" ".join(['kprof',prof_file_path,"&"]))

@staticmethod
@redirectStdout
def LoadProfFile(prof_file_path):
### lauch embedded prof editor
stats = hotshot.stats.load(prof_file_path)
stats = pstats.Stats(prof_file_path)
# Clean up filenames for the report
stats.strip_dirs()
stats.sort_stats('time', 'calls')
stats.print_stats(100)
# Sort the statistics by the cumulative time spent in the function
stats.sort_stats('cumulative')
stats.print_stats()

###
def OnDeleteProfiles(self, event):
Expand Down