| // 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" |
| "flag" |
| "fmt" |
| "os" |
| "strings" |
| "time" |
| |
| symbolize "go.fuchsia.dev/fuchsia/tools/debug/symbolize/lib" |
| "go.fuchsia.dev/fuchsia/tools/lib/cache" |
| "go.fuchsia.dev/fuchsia/tools/lib/color" |
| "go.fuchsia.dev/fuchsia/tools/lib/logger" |
| ) |
| |
| type argList []string |
| |
| func (a *argList) String() string { |
| return fmt.Sprintf("%s", []string(*a)) |
| } |
| |
| func (a *argList) Set(value string) error { |
| *a = append(*a, value) |
| return nil |
| } |
| |
| const ( |
| symbolCacheSize = 100 |
| defaultTimeout = 5 * time.Second |
| ) |
| |
| var ( |
| symbolServers argList |
| symbolCache string |
| buildIDDirPaths argList |
| colors color.EnableColor |
| jsonOutput string |
| idsPaths argList |
| // TODO(jakehehrlich): Make idsRel always true and remove this flag. |
| idsRel bool |
| level logger.LogLevel |
| llvmSymboPath string |
| llvmSymboRestartInterval uint |
| cloudFetchTimeout time.Duration |
| ) |
| |
| func init() { |
| colors = color.ColorAuto |
| level = logger.InfoLevel |
| |
| flag.Var(&symbolServers, "symbol-server", "a GCS URL or bucket name that contains debug binaries indexed by build ID") |
| flag.StringVar(&symbolCache, "symbol-cache", "", "path to directory to store cached debug binaries in") |
| flag.Var(&buildIDDirPaths, "build-id-dir", "path to .build-id directory") |
| flag.StringVar(&llvmSymboPath, "llvm-symbolizer", "llvm-symbolizer", "path to llvm-symbolizer") |
| flag.Var(&idsPaths, "ids", "path to ids.txt") |
| flag.Var(&colors, "color", "use color in output, can be never, auto, always") |
| flag.Var(&level, "level", "output verbosity, can be fatal, error, warning, info, debug or trace") |
| flag.StringVar(&jsonOutput, "json-output", "", "outputs trigger information to the specified file") |
| flag.BoolVar(&idsRel, "ids-rel", false, "tells the symbolizer to always use ids.txt relative paths") |
| flag.UintVar(&llvmSymboRestartInterval, "llvm-symbolizer-restart-interval", 15, |
| "How many queries to make to the llvm-symbolizer tool before restarting it. 0 means never restart it. Use to control memory usage. See fxbug.dev/42018.") |
| flag.DurationVar(&cloudFetchTimeout, "symbol-server-timeout", defaultTimeout, "Symbol server timeout for fetching an object from gs") |
| } |
| |
| func main() { |
| // Parse flags and setup helpers |
| flag.Parse() |
| |
| // Setup logger and context |
| painter := color.NewColor(colors) |
| log := logger.NewLogger(level, painter, os.Stdout, os.Stderr, "") |
| ctx := logger.WithLogger(context.Background(), log) |
| |
| // Construct the nodes of the pipeline |
| symbolizer := symbolize.NewLLVMSymbolizer(llvmSymboPath, llvmSymboRestartInterval) |
| var repo symbolize.CompositeRepo |
| for _, dir := range buildIDDirPaths { |
| repo.AddRepo(symbolize.NewBuildIDRepo(dir)) |
| } |
| for _, idsPath := range idsPaths { |
| repo.AddRepo(symbolize.NewIDsTxtRepo(idsPath, idsRel)) |
| } |
| var filecache *cache.FileCache |
| if len(symbolServers) > 0 { |
| if symbolCache == "" { |
| log.Fatalf("-symbol-cache must be set if a symbol server is used") |
| } |
| var err error |
| if filecache, err = cache.GetFileCache(symbolCache, symbolCacheSize); err != nil { |
| log.Fatalf("%v\n", err) |
| } |
| } |
| for _, symbolServer := range symbolServers { |
| // TODO(atyfto): Remove when all consumers are passing GCS URLs. |
| if !strings.HasPrefix(symbolServer, "gs://") { |
| symbolServer = "gs://" + symbolServer |
| } |
| cloudRepo, err := symbolize.NewCloudRepo(ctx, symbolServer, filecache) |
| if err != nil { |
| log.Fatalf("%v\n", err) |
| } |
| // Set a timeout for fetching an object from gs, defaults to 5 seconds |
| cloudRepo.SetTimeout(cloudFetchTimeout) |
| repo.AddRepo(cloudRepo) |
| } |
| demuxer := symbolize.NewDemuxer(&repo, symbolizer) |
| presenter := symbolize.NewBasicPresenter(os.Stdout, painter.Enabled()) |
| |
| tap := symbolize.NewTriggerTap() |
| var dumpHandler *symbolize.DumpHandler |
| if jsonOutput != "" { |
| dumpHandler = &symbolize.DumpHandler{} |
| tap.AddHandler(dumpHandler.HandleDump) |
| } |
| |
| // Build the pipeline to start presenting. |
| if err := symbolizer.Start(ctx); err != nil { |
| log.Fatalf("%v\n", err) |
| } |
| inputLines := symbolize.StartParsing(ctx, os.Stdin) |
| outputLines := demuxer.Start(ctx, inputLines) |
| trash := symbolize.ComposePostProcessors(ctx, outputLines, |
| tap, |
| &symbolize.ContextPresenter{}, |
| &symbolize.OptimizeColor{}, |
| symbolize.NewBacktracePresenter(os.Stdout, presenter)) |
| symbolize.Consume(trash) |
| |
| // Once the pipeline has finished output all triggers |
| if jsonOutput != "" { |
| file, err := os.Create(jsonOutput) |
| if err != nil { |
| log.Fatalf("%v\n", err) |
| } |
| if err := dumpHandler.Write(file); err != nil { |
| log.Fatalf("%v\n", err) |
| } |
| } |
| } |