Skip to content

Commit

Permalink
Fix crash when importing big linux perf tool files
Browse files Browse the repository at this point in the history
Currently, importing files generated by linux perf tool whose some blocks exceed V8 strings limit can crash the application. This issue is similar to the one in #385.

This PR fixes it by changing parseEvents to work directly with lines instead of chunking lines into blocks first.

Fixes #433
  • Loading branch information
Goose97 committed Jun 28, 2023
1 parent bb063e4 commit 19186d1
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 35 deletions.
6 changes: 3 additions & 3 deletions src/import/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {decodeBase64} from '../lib/utils'
import {importFromChromeHeapProfile} from './v8heapalloc'
import {isTraceEventFormatted, importTraceEvents} from './trace-event'
import {importFromCallgrind} from './callgrind'
import {importFromPapyrus} from "./papyrus";
import {importFromPapyrus} from './papyrus'

export async function importProfileGroupFromText(
fileName: string,
Expand Down Expand Up @@ -189,8 +189,8 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise<Profi
return toGroup(importFromInstrumentsDeepCopy(contents))
}

if (/^(Stack_|Script_|Obj_)\S+ log opened \(PC\)\n/.exec(contents.firstChunk())){
console.log("Importing as Papyrus profile")
if (/^(Stack_|Script_|Obj_)\S+ log opened \(PC\)\n/.exec(contents.firstChunk())) {
console.log('Importing as Papyrus profile')
return toGroup(importFromPapyrus(contents))
}

Expand Down
50 changes: 18 additions & 32 deletions src/import/linux-tools-perf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,23 @@ interface PerfEvent {
stack: PerfStackFrame[]
}

function parseEvent(rawEvent: string): PerfEvent | null {
const lines = rawEvent.split('\n').filter(l => !/^\s*#/.exec(l))
function parseEvents(contents: TextFileContent): (PerfEvent | null)[] {
let events: (PerfEvent | null)[] = []
let buffer: string[] = []
for (let line of contents.splitLines()) {
if (line === '') {
events.push(parseEvent(buffer))
buffer = []
} else buffer.push(line)
}

if (buffer.length > 0) events.push(parseEvent(buffer))
return events
}

// rawEvent is splitted into lines
function parseEvent(rawEvent: string[]): PerfEvent | null {
const lines = rawEvent.filter(l => !/^\s*#/.exec(l))

const event: PerfEvent = {
command: null,
Expand Down Expand Up @@ -81,41 +96,12 @@ function parseEvent(rawEvent: string): PerfEvent | null {
return event
}

function splitBlocks(contents: TextFileContent): string[] {
// In perf files, blocks are separated by '\n\n'. If our input was a simple
// string, we could use str.split('\n\n'), but since we have a TextFileContent
// object which may be backed by several strings, we can't easily split this
// way.
//
// Instead, we'll split into lines, and then re-group runs of non-empty strings.
const blocks: string[] = []
let pending: string = ''
for (let line of contents.splitLines()) {
if (line === '') {
if (pending.length > 0) {
blocks.push(pending)
}
pending = line
} else {
if (pending.length > 0) {
pending += '\n'
}
pending += line
}
}
if (pending.length > 0) {
blocks.push(pending)
}
return blocks
}

export function importFromLinuxPerf(contents: TextFileContent): ProfileGroup | null {
const profiles = new Map<string, StackListProfileBuilder>()

let eventType: string | null = null
const events = splitBlocks(contents).map(parseEvent)

for (let event of events) {
for (let event of parseEvents(contents)) {
if (event == null) continue
if (eventType != null && eventType != event.eventType) continue
if (event.time == null) continue
Expand Down

0 comments on commit 19186d1

Please sign in to comment.