blob: 2636bf0262825115f025f9216c57b559ef45ce80 [file] [log] [blame]
// Copyright 2018 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 main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"fuchsia.googlesource.com/tools/color"
"fuchsia.googlesource.com/tools/logger"
"fuchsia.googlesource.com/tools/symbolize"
)
var (
llvmSymboPath string
idsPath string
colorValue string
jsonOutput string
)
func init() {
flag.StringVar(&llvmSymboPath, "llvm-symbolizer", "llvm-symbolizer", "path to llvm-symbolizer")
flag.StringVar(&idsPath, "ids", "", "path to ids.txt")
flag.StringVar(&colorValue, "color", "auto", "can be `always`, `auto`, or `never`.")
flag.StringVar(&jsonOutput, "json-output", "", "outputs trigger information to the specified file")
}
func getColor() (color.Color, error) {
ec := color.EnableColor(colorValue)
switch ec {
case color.ColorAlways, color.ColorNever, color.ColorAuto:
return color.NewColor(ec), nil
default:
return nil, fmt.Errorf("invalid color option `%s`, possible values are `always`, `auto`, and `never`", colorValue)
}
}
type dumpEntry struct {
Modules []symbolize.Module `json:"modules"`
Segments []symbolize.Segment `json:"segments"`
Type string `json:"type"`
Name string `json:"name"`
}
type dumpHandler struct {
dumps []dumpEntry
}
func (d *dumpHandler) HandleDump(dump *symbolize.DumpfileElement) {
triggerCtx := dump.Context()
d.dumps = append(d.dumps, dumpEntry{
Modules: triggerCtx.Mods,
Segments: triggerCtx.Segs,
Type: dump.SinkType(),
Name: dump.Name(),
})
}
func (d *dumpHandler) Write(buf io.Writer) error {
enc := json.NewEncoder(buf)
enc.SetIndent("", " ")
err := enc.Encode(d.dumps)
if err != nil {
return err
}
return nil
}
func main() {
// Parse flags and setup helpers
flag.Parse()
painter, err := getColor()
if err != nil {
log.Fatal(err)
}
var jsonTriggerHandler *dumpHandler
if jsonOutput != "" {
jsonTriggerHandler = &dumpHandler{}
}
// Setup logger and context
symbolizeLogger := logger.NewLogger(logger.WarningLevel, painter, os.Stderr, os.Stderr)
ctx := logger.WithLogger(context.Background(), symbolizeLogger)
// Construct the nodes of the pipeline
symbolizer := symbolize.NewLLVMSymbolizer(llvmSymboPath)
repo := symbolize.NewRepo()
err = repo.AddSource(symbolize.NewIDsSource(idsPath))
if err != nil {
symbolizeLogger.Fatalf("%v", err)
}
demuxer := symbolize.NewDemuxer(repo, symbolizer)
tap := symbolize.NewTriggerTap()
if jsonTriggerHandler != nil {
tap.AddHandler(jsonTriggerHandler.HandleDump)
}
presenter := symbolize.NewBasicPresenter(os.Stdout, painter.Enabled())
// Build the pipeline to start presenting.
err = symbolizer.Start(ctx)
if err != nil {
symbolizeLogger.Fatalf("%v", err)
}
inputLines := symbolize.StartParsing(ctx, os.Stdin)
outputLines := demuxer.Start(ctx, inputLines)
trash := symbolize.ComposePostProcessors(ctx, outputLines,
tap,
&symbolize.FilterContextElements{},
&symbolize.OptimizeColor{},
symbolize.NewBacktracePresenter(os.Stdout, presenter))
symbolize.Consume(trash)
// Once the pipeline has finished output all triggers
if jsonTriggerHandler != nil {
file, err := os.Open(jsonOutput)
if err != nil {
log.Fatal(err)
}
jsonTriggerHandler.Write(file)
}
}