Python API 2.0 Reference
python/api1/profilerDump.py
1 from builtins import range
2 import maya.OpenMaya as om
3 import json
4 import csv
5 
6 __all__ = [ 'profilerToJSON', 'profilerToCSV', 'profilerFormatJSON']
7 
8 #
9 # The following is sample code which uses the OpenMaya API to show how
10 # data from Maya's profiler can be output to file on disk in a custom text format.
11 #
12 # Note that references to Maya's profiler output format are valid at the time of writing.
13 #
14 # The format's chosen to illustrate this are JSON and CSV. For each sample output is provided.
15 
16 # Sample 1: Profiler to JSON output
17 # ---------------------------------
18 #
19 def profilerToJSON(fileName, useIndex, durationMin):
20  """
21  fileName : name of file to write to disk
22  useIndex : write events using index lookup to category and name lists
23  durationMin : only write out events which have at least this minimum time duration
24 
25  Description:
26  Sample code to extract profiler information and write to file in JSON format
27 
28  Example usage:
29  > profilerToJSON('profiler_indexed.json', True, 0.0) # Index without a duration clamp
30  > profilerToJSON('profiler_nonIndexed.json', False, 10.0) # Non-Indexed with duration clamp
31  """
32  # Below is some sample output from after running the JSON export. Note that here we try to mimic
33  # the data and ordering of Maya's internal profiler save by storing category and event name lists
34  # and specifying lookups for each event. e.g. the first event stores a name index (nameIdx) of 59
35  # which would reference element 59 in "eventNames".
36  #
37  # Note that the total available events and the number of events actually written out is included.
38  #
39  # {
40  # "version": 1,
41  # "eventCount": 11276,
42  # "cpuCount": 8,
43  # "categories": ["Main", "Change", "Dirty Propagation", "Evaluation", "VP1 Evaluation", "Poly", "Mudbox", "VP2 Evaluation", "Qt", "Render Setup", "Anim UI Refresh"],
44  # "eventNames": ["QtDynamicPropertyChange", "PreRefreshTopLevel", "QtCreate", "Vp2ShaderDoDG", "QtShortcutOverride", "Vp2SceneRender", "Vp2ShaderItemCompileShader", "QtShowToParent", "QtMove", "Vp2AssignShaderInstance", "opaqueUIList", "Vp2ParallelEvaluationTask", "QtResize", "colorPass", "pConeShape1", "QtWindowDeactivate", "QtZeroTimerEvent", "QtTimer", "QtNonClientAreaMouseButtonPress", "QtMouseButtonRelease", "QtChildRemoved", "QtApplicationDeactivate", "Vp2ShaderWrite", "QtChildAdded", "QtWindowActivate", "Vp2PrepareToUpdate", "QtMetaCall", "QtActivationChange", "QtUpdateLater", "QtFocusIn", "QtShow", "QtUserEvent", "QtWindowStateChange", "QtModifiedChange", "Vp2UpdateGeometryBuffer", "setDirty", "Vp2AcquireOffScreenTarget", "QtKeyRelease", "uiGeometry", "Vp2UpdateUI", "QtUpdateRequest", "QtToolTipChange", "QtApplicationPaletteChange", "QtDragMove", "preUIGeometry", "Vp2WaitForEvaluation", "QtKeyPress", "QtNonClientAreaMouseButtonRelease", "QtLeave", "opaqueGeometry", "initialShadingGroup", "QtDrop", "Vp2BuildRenderLists", "Vp2UpdateGeometry", "Compute", "Vp2ClearRenderLists", "Vp2UpdateScene", "postUIGeometry", "QtMouseButtonPress", "QtIdleTimer", "QtDragEnter", "QtDestroy", "QtPaint", "Vp2BuildShadowMaps", "Vp2UpdateDagObject", "QtEnter", "pPyramidShape1", "QtMouseMove", "Vp2TranslateGeometry", "QtWindowTitleChange", "QtZOrderChange", "ScheduleRefreshAllViews", "QtDeferredDelete", "QtPaletteChange", "QtNonClientAreaMouseMove", "shadedBeautyGraphSemantic", "QtApplicationActivate", "QtCursorChange", "Vp2WaitForTranslation", "sendAttributeChangedMsg", "QtLayoutRequest", "QtStatusTip", "QtDragLeave", "QtFocusOut", "Vp2Draw3dBeautyPass", "Vp2HUD", "Vp2ConstructFragmentGraph", "QtHide"],
45  # "events": [{
46  # "time": 25610841341,
47  # "nameIdx": 59,
48  # "desc": "",
49  # "catIdx": 8,
50  # "duration": 39014,
51  # "tDuration": 39724089,
52  # "tId": 6972,
53  # "cpuId": 4,
54  # "colorId": 6
55  # }, {
56  # "time": 25620057925,
57  # "nameIdx": 79,
58  # "desc": "",
59  # "catIdx": 1,
60  # "duration": 228,
61  # "tDuration": 212774,
62  # "tId": 6972,
63  # "cpuId": 0,
64  # "colorId": 12
65  # }
66  # .... more events ...
67  # ]
68  # "eventsWritten": 364
69  # }
70  #
71  # Here is the same data without using indexing.
72  # {
73  # "cpuCount": 8,
74  # "eventCount": 11276,
75  # "events": [
76  # {
77  # "category": "Qt",
78  # "colorId": 6,
79  # "cpuId": 4,
80  # "desc": "",
81  # "duration": 39014,
82  # "name": "QtIdleTimer",
83  # "tDuration": 39724089,
84  # "tId": 6972,
85  # "time": 25610841341
86  # },
87  # {
88  # "category": "Change",
89  # "colorId": 12,
90  # "cpuId": 0,
91  # "desc": "",
92  # "duration": 228,
93  # "name": "sendAttributeChangedMsg",
94  # "tDuration": 212774,
95  # "tId": 6972,
96  # "time": 25620057925
97  # }
98  # .... more events ...
99  # ]
100  # "eventsWritten": 364
101  #
102  stripped = lambda s: "".join(i for i in s if 31 < ord(i) < 127)
103 
104  eventCount = om.MProfiler.getEventCount()
105  if eventCount == 0:
106  return
107 
108  file = open(fileName, "w")
109  if not file:
110  return
111 
112 
113  file.write("{\n")
114 
115  # Output version
116  file.write("\t\"version\": 1,\n")
117  # Output event count
118  file.write("\t\"eventCount\": " + str(eventCount) + ",\n")
119  # Output number of CPUS. Missing from Python API
120  file.write("\t\"cpuCount\": " + str(om.MProfiler.getNumberOfCPUs()) + ",\n")
121 
122  # Output event categories if using indexing lookup
123  categories = []
124  om.MProfiler.getAllCategories(categories)
125  asciiString = json.dumps(categories, True, True)
126  if useIndex:
127  file.write("\t\"categories\": " + asciiString + ",\n")
128 
129  # Output event name list if using indexing
130  nameDict = {}
131  for i in range(0, eventCount, 1):
132  eventName = om.MProfiler.getEventName(i)
133  eventName = eventName.decode('ascii', 'replace')
134  eventName = stripped(eventName)
135  if eventName not in nameDict:
136  nameDict[eventName] = len(nameDict)
137  if useIndex:
138  nameString = json.dumps(list(nameDict.keys()), True, True)
139  file.write('\t\"eventNames\" : ' + nameString + ",\n")
140 
141  # Write out each event:
142  # Event time, Event Name / Event Index, Description , Category / Category index, Duration, Thread Duration, Thread id, Cpu id, Color id
143  file.write('\t\"events\": [\n')
144  dumped = False
145  eventsWritten = 0
146  for i in range(0, eventCount):
147 
148  duration = om.MProfiler.getEventDuration(i)
149  if duration > durationMin:
150  eventsWritten = eventsWritten + 1
151 
152  eventTime = om.MProfiler.getEventTime(i)
153  eventName = om.MProfiler.getEventName(i)
154  eventName = eventName.decode('ascii', 'replace')
155  eventName = stripped(eventName)
156  if useIndex:
157  eventNameIndex = list(nameDict.keys()).index(eventName)
158 
159  description = ''
160  if om.MProfiler.getDescription(i):
161  description = om.MProfiler.getDescription(i)
162 
163  eventCategory = om.MProfiler.getEventCategory(i)
164  eventCategoryName = om.MProfiler.getCategoryName(eventCategory)
165  if useIndex:
166  eventCatagoryIndex = categories.index(eventCategoryName)
167 
168  threadDuration = om.MProfiler.getThreadDuration(i)
169 
170  threadId = om.MProfiler.getThreadId(i)
171 
172  cpuId = om.MProfiler.getCPUId(i)
173 
174  colorId = om.MProfiler.getColor(i)
175 
176  # Instead of using json library, the code just writes on the fly
177  if dumped:
178  file.write('\t,{ ')
179  else:
180  file.write('\t{ ')
181  dumped = True
182  file.write('\"time\" : ' + str(eventTime) + ', ')
183  if useIndex:
184  file.write('\"nameIdx\" : ' + str(eventNameIndex) + ', ')
185  else:
186  file.write('\"name\" : \"' + eventName + '\", ')
187  file.write('\"desc\" : \"' + str(description) + '\", ')
188  if useIndex:
189  file.write('\"catIdx\" : ' + str(eventCatagoryIndex) + ', ')
190  else:
191  file.write('\"category\" : \"' + eventCategoryName + '\", ')
192  file.write('\"duration\" : ' + str(duration) + ', ')
193  file.write('\"tDuration\" : ' + str(threadDuration) + ', ')
194  file.write('\"tId\" : ' + str(threadId) + ', ')
195  file.write('\"cpuId\" : ' + str(cpuId) + ', ')
196  file.write('\"colorId\" : ' + str(colorId) + '')
197  file.write('\t}\n')
198 
199  file.write("\t],\n")
200  file.write("\t\"eventsWritten\": " + str(eventsWritten) + "\n")
201  file.write("}\n")
202  file.close()
203 
204 def profilerFormatJSON(fileName, fileName2):
205  """
206  fileName : name of file to read
207  fileName2 : name of file to write to
208 
209  Description:
210  Simple utility code to read a JSON file sort and format it before
211  writing to a secondary file.
212 
213  Example:
214  > profilerFormatJSON('profilerIn.json', 'profilerFormatted.json')
215 
216  """
217  file = open(fileName, "r")
218  if not file:
219  return
220 
221  result = json.load(file)
222  file.close()
223 
224  dump = json.dumps(result, sort_keys=True, indent=4, separators=(',', ': '))
225 
226  file2 = open(fileName2, "w")
227  if not file2:
228  return
229 
230  file2.write(dump)
231  file2.close()
232 
233 
234 
235 
236 # Sample 1: Profiler to CSV output
237 # ---------------------------------
238 #
239 def profilerToCSV(fileName, durationMin):
240  """
241  fileName : name of file to write to disk
242  useIndex : write events using index lookup to category and name lists
243  durationMin : only write out events which have at least this minimum time duration
244 
245  Description:
246  Sample to output profiler event information only to CSV format.
247  Example:
248  > profilerToCSV('profiler.csv', 0.0)
249  """
250  #
251  # Sample output:
252  #
253  # Event Time","Event Name","Description","Event Category","Duration","Thread Duration","Thread Id","CPU Id","Color Id"
254  # 25610841341,"QtIdleTimer","","Qt",39014,39724089,6972,4,6
255  # 25620057925,"sendAttributeChangedMsg","","Change",228,212774,6972,0,12
256  # 25620058186,"setDirty","","Dirty Propagation",8,7806,6972,0,1
257  # 25620058276,"sendAttributeChangedMsg","","Change",11,10633,6972,0,12
258  # 25620058310,"sendAttributeChangedMsg","","Change",8,7732,6972,0,12
259  # 25620058332,"sendAttributeChangedMsg","","Change",7,6844,6972,0,12
260  # ... <more events> ...
261  #
262  stripped = lambda s: "".join(i for i in s if 31 < ord(i) < 127)
263 
264  eventCount = om.MProfiler.getEventCount()
265  if eventCount == 0:
266  return
267 
268  file = open(fileName, "w")
269  if not file:
270  return
271 
272  csvWriter = csv.writer(file, quoting=csv.QUOTE_NONNUMERIC)
273 
274  # Write out each event:
275  # Event time, Event Name / Event Index, Description , Category / Category index, Duration, Thread Duration, Thread id, Cpu id, Color id
276 
277  head = ( 'Event Time', 'Event Name', 'Description', 'Event Category', 'Duration', 'Thread Duration', 'Thread Id', 'CPU Id', 'Color Id' )
278  csvWriter.writerow(head)
279 
280  for i in range(0, eventCount):
281 
282  duration = om.MProfiler.getEventDuration(i)
283  if duration > durationMin:
284 
285  eventTime = om.MProfiler.getEventTime(i)
286  eventName = om.MProfiler.getEventName(i)
287  eventName = eventName.decode('ascii', 'replace')
288  eventName = stripped(eventName)
289 
290  description = ''
291  if om.MProfiler.getDescription(i):
292  description = om.MProfiler.getDescription(i)
293 
294  eventCategory = om.MProfiler.getEventCategory(i)
295  eventCategoryName = om.MProfiler.getCategoryName(eventCategory)
296 
297  threadDuration = om.MProfiler.getThreadDuration(i)
298 
299  threadId = om.MProfiler.getThreadId(i)
300 
301  cpuId = om.MProfiler.getCPUId(i)
302 
303  colorId = om.MProfiler.getColor(i)
304 
305  row = ( eventTime, eventName, description, eventCategoryName, duration, threadDuration, threadId, cpuId, colorId )
306 
307  csvWriter.writerow(row)
308 
309  file.close()
310 
311 
312 # Nothing run on initialize for now
313 def initializePlugin(obj):
314  obj
315 
316 def uninitializePlugin(obj):
317  obj