From f3a1c09c9b21644f816b90035fbe198cc787eefd Mon Sep 17 00:00:00 2001 From: Jamie Wong Date: Tue, 29 Sep 2020 14:26:01 -0700 Subject: [PATCH] Add support for Safari profiles (#313) Closes #294 This adds import for Safari/webkit profiler. Well, for Safari 13.1 for sure, I haven't done any work to check if there's been changes to the syntax. It seems to work OK, and is already a huge improvement over profiling in Safari (which doesn't even have a flame graph, let alone something like left heavy). Sadly, the sampler resolution is only 1kHz, which is not super useful for a lot of profiling work. I made a ticket on webkit bug tracker to ask for 10kHz/configurable sampling rate: https://bugs.webkit.org/show_bug.cgi?id=214866 Another thing that's missing is that I cut out all the idle time. We could also insert layout/paint samples into the timeline by parsing `events`. But I'll leave that for another time. Captura de pantalla 2020-07-28 a las 11 02 06 --- README.md | 1 + .../Safari/13.1/simple.html-recording.json | 1 + src/import/__snapshots__/safari.test.ts.snap | 101 +++++++++++++++ src/import/index.ts | 7 + src/import/safari.test.ts | 5 + src/import/safari.ts | 120 ++++++++++++++++++ 6 files changed, 235 insertions(+) create mode 100644 sample/profiles/Safari/13.1/simple.html-recording.json create mode 100644 src/import/__snapshots__/safari.test.ts.snap create mode 100644 src/import/safari.test.ts create mode 100644 src/import/safari.ts diff --git a/README.md b/README.md index 39c05d2..471bdc0 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ speedscope is designed to ingest profiles from a variety of different profilers - JavaScript - [Importing from Chrome](https://github.com/jlfwong/speedscope/wiki/Importing-from-Chrome) - [Importing from Firefox](https://github.com/jlfwong/speedscope/wiki/Importing-from-Firefox) + - [Importing from Safari](https://github.com/jlfwong/speedscope/wiki/Importing-from-Safari) - [Importing from Node.js](https://github.com/jlfwong/speedscope/wiki/Importing-from-Node.js) - Ruby - [Importing from stackprof](https://github.com/jlfwong/speedscope/wiki/Importing-from-stackprof-(ruby)) diff --git a/sample/profiles/Safari/13.1/simple.html-recording.json b/sample/profiles/Safari/13.1/simple.html-recording.json new file mode 100644 index 0000000..44f081f --- /dev/null +++ b/sample/profiles/Safari/13.1/simple.html-recording.json @@ -0,0 +1 @@ +{"version":1,"recording":{"displayName":"Grabación de Control temporal 1","startTime":4.163978071999736,"endTime":6.692470462992787,"discontinuities":[],"instrumentTypes":["timeline-record-type-network","timeline-record-type-layout","timeline-record-type-script","timeline-record-type-rendering-frame","timeline-record-type-cpu"],"records":[{"type":"timeline-record-type-cpu","timestamp":4.174319899990223,"usage":0,"threads":[{"name":"Main Thread","usage":0,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-network","archiveStartTime":1595926028.6345506,"entry":{"pageref":"page_0","startedDateTime":"2020-07-28T08:47:13.097Z","time":0,"request":{"method":"GET","url":"file:///speedscope/sample/programs/javascript/simple.html","httpVersion":"","cookies":[],"headers":[{"name":"Upgrade-Insecure-Requests","value":"1"},{"name":"Accept","value":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},{"name":"User-Agent","value":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15"}],"queryString":[],"headersSize":0,"bodySize":0},"response":{"status":0,"statusText":"","httpVersion":"","cookies":[],"headers":[],"content":{"size":0,"compression":0,"mimeType":"text/html"},"redirectURL":"","headersSize":0,"bodySize":0,"_transferSize":0},"cache":{},"timings":{"blocked":0,"dns":-1,"connect":-1,"ssl":-1,"send":0,"wait":0,"receive":0},"_fetchType":"Network Load","_priority":"high"}},{"type":"timeline-record-type-network","archiveStartTime":1595926028.6345935,"entry":{"pageref":"page_0","startedDateTime":"2020-07-28T08:47:13.126Z","time":0,"request":{"method":"GET","url":"file:///speedscope/sample/programs/javascript/simple.js","httpVersion":"","cookies":[],"headers":[{"name":"User-Agent","value":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15"},{"name":"Accept","value":"*/*"}],"queryString":[],"headersSize":0,"bodySize":0},"response":{"status":0,"statusText":"","httpVersion":"","cookies":[],"headers":[],"content":{"size":0,"compression":0,"mimeType":"text/javascript"},"redirectURL":"","headersSize":0,"bodySize":0,"_transferSize":0},"cache":{},"timings":{"blocked":0,"dns":-1,"connect":-1,"ssl":-1,"send":0,"wait":0,"receive":0},"_fetchType":"Network Load","_priority":"high"}},{"type":"timeline-record-type-rendering-frame","startTime":4.467359638976632,"endTime":4.492321582976729},{"type":"timeline-record-type-layout","eventType":"recalculate-styles","startTime":4.471605148981325,"endTime":4.471845255000517},{"type":"timeline-record-type-layout","eventType":"invalidate-styles","startTime":4.474205192993395,"endTime":4.474205192993395},{"type":"timeline-record-type-layout","eventType":"recalculate-styles","startTime":4.474220764997881,"endTime":4.4884890760004055},{"type":"timeline-record-type-layout","eventType":"invalidate-styles","startTime":4.491480771976057,"endTime":4.491480771976057},{"type":"timeline-record-type-rendering-frame","startTime":4.492437198990956,"endTime":4.507455001003109},{"type":"timeline-record-type-layout","eventType":"recalculate-styles","startTime":4.492771623976296,"endTime":4.507425039977534},{"type":"timeline-record-type-rendering-frame","startTime":4.507473898003809,"endTime":4.5526633959962055},{"type":"timeline-record-type-script","eventType":"script-evaluated","startTime":4.513065324979834,"endTime":4.545594235998578,"details":"","extraDetails":null},{"type":"timeline-record-type-layout","eventType":"invalidate-styles","startTime":4.545641086995602,"endTime":4.545641086995602},{"type":"timeline-record-type-layout","eventType":"recalculate-styles","startTime":4.545677282992983,"endTime":4.545750883989967},{"type":"timeline-record-type-layout","eventType":"invalidate-layout","startTime":4.545747961994493,"endTime":4.545747961994493},{"type":"timeline-record-type-layout","eventType":"layout","startTime":4.545889647997683,"endTime":4.546186422987375,"quad":[0,0,1153,0,1153,814,0,814]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.547691310988739,"endTime":4.5477759249915835,"quad":[1024,575,1153,575,1153,877,1024,877]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.5483486430021,"endTime":4.548382615990704,"quad":[512,575,1024,575,1024,877,512,877]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.54878898098832,"endTime":4.548809086001711,"quad":[0,575,512,575,512,877,0,877]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.549124169978313,"endTime":4.549146869976539,"quad":[1024,63,1153,63,1153,575,1024,575]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.549656325980322,"endTime":4.5496779779787175,"quad":[512,63,1024,63,1024,575,512,575]},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.5500254239887,"endTime":4.5500440479954705,"quad":[0,63,512,63,512,575,0,575]},{"type":"timeline-record-type-layout","eventType":"composite","startTime":4.550935890991241,"endTime":4.551078663003864},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.555242645990802,"endTime":4.555461437994381,"details":"","extraDetails":null},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.572847526986152,"endTime":4.573226868000347,"details":"","extraDetails":null},{"type":"timeline-record-type-rendering-frame","startTime":4.621030936978059,"endTime":4.621331342990743},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.62123295100173,"endTime":4.621266011003172,"quad":[0,63,1153,63,1153,877,0,877]},{"type":"timeline-record-type-cpu","timestamp":4.676897135999752,"usage":21.69999885559082,"threads":[{"name":"Main Thread","usage":18.600000381469727,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0.30000001192092896},{"name":"","usage":0.6000000238418579},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0.6000000238418579},{"name":"","usage":0.10000000149011612},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0.30000001192092896,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0.800000011920929,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0.4000000059604645,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.79833571598283,"endTime":4.798853140004212,"details":"","extraDetails":null},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.8038428709842265,"endTime":4.803945658000885,"details":"","extraDetails":null},{"type":"timeline-record-type-script","eventType":"api-script-evaluated","startTime":4.807004723988939,"endTime":4.807019564992515,"details":"","extraDetails":null},{"type":"timeline-record-type-rendering-frame","startTime":4.92319532399415,"endTime":4.923449570982484},{"type":"timeline-record-type-layout","eventType":"paint","startTime":4.923351720004575,"endTime":4.923385464993771,"quad":[0,63,1153,63,1153,877,0,877]},{"type":"timeline-record-type-cpu","timestamp":5.17931115499232,"usage":3.999999761581421,"threads":[{"name":"Main Thread","usage":3.700000047683716,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0.10000000149011612},{"name":"","usage":0.10000000149011612},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0.10000000149011612,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-cpu","timestamp":5.683525055996142,"usage":0.6000000238418579,"threads":[{"name":"Main Thread","usage":0.6000000238418579,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-cpu","timestamp":6.188329044991406,"usage":0,"threads":[{"name":"Main Thread","usage":0,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]},{"type":"timeline-record-type-cpu","timestamp":6.692470462992787,"usage":0,"threads":[{"name":"Main Thread","usage":0,"type":"main"},{"name":"JavaScriptCore bmalloc scavenger","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"WebCore: Scrolling","usage":0,"type":"webkit"},{"name":"","usage":0},{"name":"","usage":0},{"name":"","usage":0},{"name":"JIT Worklist Helper Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"DFG Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"FTL Worklist Worker Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"},{"name":"Heap Helper Thread","usage":0,"type":"webkit"}]}],"markers":[{"time":4.545802743989043,"type":"dom-content-event"},{"time":4.545883116981713,"type":"load-event"}],"memoryPressureEvents":[],"sampleStackTraces":[{"timestamp":4.472667262976756,"stackFrames":[{"sourceID":"7","name":"","line":2,"column":10,"url":"__InjectedScript_CommandLineAPIModuleSource.js","expressionLocation":{"line":34,"column":13}},{"sourceID":"6","name":"injectModule","line":109,"column":13,"url":"__InjectedScript_InjectedScriptSource.js","expressionLocation":{"line":111,"column":85}}]},{"timestamp":4.514140182000119,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.516020926996134,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.5172975349996705,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.518430622003507,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.519961087004049,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.521057823993033,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.523174777976237,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.527184398990357,"stackFrames":[{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":15,"column":19}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.528929433989106,"stackFrames":[{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":15,"column":19}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.529900363995694,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.530873391981004,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.532054496987257,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.533258385985391,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.534531568002421,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.536061600985704,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.537377392989583,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.538861920998897,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.540356789977523,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.541859452001518,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.543215062993113,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"delta","line":14,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":16,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":4,"column":10}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.5444030729995575,"stackFrames":[{"sourceID":"8","name":"gamma","line":20,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":22,"column":19}},{"sourceID":"8","name":"beta","line":8,"column":14,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":10,"column":10}},{"sourceID":"8","name":"alpha","line":1,"column":15,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":3,"column":9}},{"sourceID":"8","name":"(program)","line":1,"column":1,"url":"file:///speedscope/sample/programs/javascript/simple.js","expressionLocation":{"line":28,"column":6}}]},{"timestamp":4.798422646999825,"stackFrames":[{"sourceID":"10","name":"firstOpenSearchURLString","line":4,"column":102,"url":"","expressionLocation":{"line":4,"column":117}}]}],"sampleDurations":[0.001,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.001547111237111191,0.0005174240213818848]},"overview":{"secondsPerPixel":0.01,"scrollStartTime":2.887978071999762,"selectionStartTime":4.163978071999736,"selectionDuration":15}} diff --git a/src/import/__snapshots__/safari.test.ts.snap b/src/import/__snapshots__/safari.test.ts.snap new file mode 100644 index 0000000..ba6d904 --- /dev/null +++ b/src/import/__snapshots__/safari.test.ts.snap @@ -0,0 +1,101 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`importFromSafari 1`] = ` +Object { + "frames": Array [ + Frame { + "col": 13, + "file": "__InjectedScript_InjectedScriptSource.js", + "key": "injectModule:__InjectedScript_InjectedScriptSource.js:109:13", + "line": 109, + "name": "injectModule", + "selfWeight": 0, + "totalWeight": 0.001, + }, + Frame { + "col": 10, + "file": "__InjectedScript_CommandLineAPIModuleSource.js", + "key": ":__InjectedScript_CommandLineAPIModuleSource.js:2:10", + "line": 2, + "name": "(anonymous)", + "selfWeight": 0.001, + "totalWeight": 0.001, + }, + Frame { + "col": 1, + "file": "file:///speedscope/sample/programs/javascript/simple.js", + "key": "(program):file:///speedscope/sample/programs/javascript/simple.js:1:1", + "line": 1, + "name": "(program)", + "selfWeight": 0, + "totalWeight": 0.03248933597933502, + }, + Frame { + "col": 15, + "file": "file:///speedscope/sample/programs/javascript/simple.js", + "key": "alpha:file:///speedscope/sample/programs/javascript/simple.js:1:15", + "line": 1, + "name": "alpha", + "selfWeight": 0, + "totalWeight": 0.03248933597933502, + }, + Frame { + "col": 15, + "file": "file:///speedscope/sample/programs/javascript/simple.js", + "key": "delta:file:///speedscope/sample/programs/javascript/simple.js:14:15", + "line": 14, + "name": "delta", + "selfWeight": 0.003094222474222382, + "totalWeight": 0.020112446082445484, + }, + Frame { + "col": 15, + "file": "file:///speedscope/sample/programs/javascript/simple.js", + "key": "gamma:file:///speedscope/sample/programs/javascript/simple.js:20:15", + "line": 20, + "name": "gamma", + "selfWeight": 0.029395113505112636, + "totalWeight": 0.029395113505112636, + }, + Frame { + "col": 14, + "file": "file:///speedscope/sample/programs/javascript/simple.js", + "key": "beta:file:///speedscope/sample/programs/javascript/simple.js:8:14", + "line": 8, + "name": "beta", + "selfWeight": 0, + "totalWeight": 0.012376889896889526, + }, + Frame { + "col": 102, + "file": "", + "key": "firstOpenSearchURLString::4:102", + "line": 4, + "name": "firstOpenSearchURLString", + "selfWeight": 0.0005174240213818848, + "totalWeight": 0.0005174240213818848, + }, + ], + "name": "Grabación de Control temporal 1", + "stacks": Array [ + "injectModule;(anonymous) 1.00ms", + " 39.93ms", + "(program);alpha;delta;gamma 10.83ms", + " 2.46ms", + "(program);alpha;delta 3.09ms", + "(program);alpha;beta;gamma 4.64ms", + "(program);alpha;delta;gamma 1.55ms", + "(program);alpha;beta;gamma 1.55ms", + "(program);alpha;delta;gamma 3.09ms", + "(program);alpha;beta;gamma 4.64ms", + "(program);alpha;delta;gamma 1.55ms", + "(program);alpha;beta;gamma 1.55ms", + " 253.50ms", + "firstOpenSearchURLString 517.42µs", + ], +} +`; + +exports[`importFromSafari: indexToView 1`] = `0`; + +exports[`importFromSafari: profileGroup.name 1`] = `"Grabación de Control temporal 1"`; diff --git a/src/import/index.ts b/src/import/index.ts index d9f09da..3f46d52 100644 --- a/src/import/index.ts +++ b/src/import/index.ts @@ -15,6 +15,7 @@ import {importSpeedscopeProfiles} from '../lib/file-format' import {importFromV8ProfLog} from './v8proflog' import {importFromLinuxPerf} from './linux-tools-perf' import {importFromHaskell} from './haskell' +import {importFromSafari} from './safari' import {ProfileDataSource, TextProfileDataSource, MaybeCompressedDataReader} from './utils' import {importAsPprofProfile} from './pprof' import {decodeBase64} from '../lib/utils' @@ -131,6 +132,9 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise { + await checkProfileSnapshot('./sample/profiles/Safari/13.1/simple.html-recording.json') +}) diff --git a/src/import/safari.ts b/src/import/safari.ts new file mode 100644 index 0000000..d83fc32 --- /dev/null +++ b/src/import/safari.ts @@ -0,0 +1,120 @@ +import {Profile, FrameInfo, StackListProfileBuilder} from '../lib/profile' +import {TimeFormatter} from '../lib/value-formatters' + +interface Record { + type: string + eventType?: string + startTime?: number + endTime?: number + // timeline-record-type-cpu + timestamp?: number + usage?: number + threads?: any[] + // timeline-record-type-script + details?: number | string | any + extraDetails?: null | any + // timeline-record-type-network + archiveStartTime?: number + entry?: any + // timeline-record-type-layout + quad?: number[] +} + +interface ExprLocation { + line: number + column: number +} + +interface StackFrame { + sourceID: string + name: string + line: number + column: number + url: string + expressionLocation?: ExprLocation +} + +interface Sample { + timestamp: number + stackFrames: StackFrame[] +} + +interface Recording { + displayName: string + startTime: number + endTime: number + discontinuities: any[] + instrumentTypes: string[] + records: Record[] + markers: any[] + memoryPressureEvents: any[] + sampleStackTraces: Sample[] + sampleDurations: number[] +} + +interface Overview { + secondsPerPixel: number + scrollStartTime: number + selectionStartTime: number + selectionDuration: number +} + +interface SafariProfile { + version: number + recording: Recording + overview: Overview +} + +function makeStack(frames: StackFrame[]): FrameInfo[] { + return frames + .map(({name, url, line, column}) => ({ + key: `${name}:${url}:${line}:${column}`, + file: url, + line, + col: column, + name: name || '(anonymous)', + })) + .reverse() +} + +export function importFromSafari(contents: SafariProfile): Profile | null { + if (contents.version !== 1) { + console.warn(`Unknown Safari profile version ${contents.version}... Might be incompatible.`) + } + + const {recording} = contents + const {sampleStackTraces, sampleDurations} = recording + + const count = sampleStackTraces.length + if (count < 1) { + console.warn('Empty profile') + return null + } + + const profileDuration = + sampleStackTraces[count - 1].timestamp - sampleStackTraces[0].timestamp + sampleDurations[0] + const profile = new StackListProfileBuilder(profileDuration) + + let previousEndTime = Number.MAX_VALUE + + sampleStackTraces.forEach((sample, i) => { + const endTime = sample.timestamp + const duration = sampleDurations[i] + const startTime = endTime - duration + const idleDurationBefore = startTime - previousEndTime + + // FIXME: 2ms is a lot, but Safari's timestamps and durations don't line up very well and will create + // phantom idle time + if (idleDurationBefore > 0.002) { + profile.appendSampleWithWeight([], idleDurationBefore) + } + + profile.appendSampleWithWeight(makeStack(sample.stackFrames), duration) + + previousEndTime = endTime + }) + + profile.setValueFormatter(new TimeFormatter('seconds')) + profile.setName(recording.displayName) + return profile.build() +}