blob: 495ab6637d5218b3ed4c039009b504f012bf93ed [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package breakpad
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"strings"
)
// ParseSymbolFile reads a breakpad symbol file.
func ParseSymbolFile(r io.Reader) (*SymbolFile, error) {
bytes, err := ioutil.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("failed to read symbol file: %v", err)
}
lines := strings.SplitN(string(bytes), "\n", 2)
if len(lines) != 2 {
return nil, fmt.Errorf("got < 2 lines in symbol file")
}
moduleSection := strings.TrimSpace(lines[0])
if moduleSection == "" {
return nil, fmt.Errorf("unexpected blank first line in symbol file")
}
module, err := parseModuleLine(moduleSection)
if err != nil {
return nil, fmt.Errorf("failed to parse module section: %v", err)
}
return &SymbolFile{
ModuleSection: module,
remainder: lines[1],
}, nil
}
// SymbolFile represents a Breakpad symbol file, typically produced by the dump_syms tool.
// This is only a partial implementation; the ModuleSection is all that is parsed. Add fields
// as needed.
type SymbolFile struct {
ModuleSection *ModuleSection
// Unparsed contents from the input file excluding the ModuleSection.
remainder string
}
func (f SymbolFile) WriteTo(w io.Writer) (int64, error) {
return io.Copy(w, f.Reader())
}
// Reader returns an io.Reader for this SymbolFile data as it would be written to a file.
func (f SymbolFile) Reader() io.Reader {
var buf bytes.Buffer
buf.WriteString(f.ModuleSection.String())
buf.WriteString("\n")
buf.WriteString(f.remainder)
return &buf
}
// ModuleSection represents the first section/line of a symbol file.
type ModuleSection struct {
OS string
Arch string
BuildID string
ModuleName string
}
// String formats this ModuleSection as it would be written to a file.
func (mod *ModuleSection) String() string {
content := fmt.Sprintf("MODULE %s %s %s %s", mod.OS, mod.Arch, mod.BuildID, mod.ModuleName)
return strings.TrimSpace(content)
}
func parseModuleLine(source string) (*ModuleSection, error) {
format := "MODULE $OS $ARCH $BUILD_ID $MODULE_NAME"
parts := strings.Split(source, " ")
if len(parts) != 5 {
return nil, fmt.Errorf("wanted line 1 with format %q but got %q", format, source)
}
return &ModuleSection{
OS: parts[1],
Arch: parts[2],
BuildID: parts[3],
ModuleName: parts[4],
}, nil
}