diff --git a/cmd/profilecli/main.go b/cmd/profilecli/main.go index 8bff759b5a..124b160cff 100644 --- a/cmd/profilecli/main.go +++ b/cmd/profilecli/main.go @@ -60,6 +60,10 @@ func main() { parquetInspectCmd := parquetCmd.Command("inspect", "Inspect a parquet file's structure.") parquetInspectFiles := parquetInspectCmd.Arg("file", "parquet file path").Required().ExistingFiles() + tsdbCmd := adminCmd.Command("tsdb", "Operate on a TSDB index file.") + tsdbSeriesCmd := tsdbCmd.Command("series", "dump series in an TSDB index file.") + tsdbSeriesFiles := tsdbSeriesCmd.Arg("file", "tsdb file path").Required().ExistingFiles() + queryCmd := app.Command("query", "Query profile store.") queryMergeCmd := queryCmd.Command("merge", "Request merged profile.") queryMergeOutput := queryMergeCmd.Flag("output", "How to output the result, examples: console, raw, pprof=./my.pprof").Default("console").String() @@ -97,6 +101,12 @@ func main() { os.Exit(checkError(err)) } } + case tsdbSeriesCmd.FullCommand(): + for _, file := range *tsdbSeriesFiles { + if err := tsdbSeries(ctx, file); err != nil { + os.Exit(checkError(err)) + } + } case queryMergeCmd.FullCommand(): if err := queryMerge(ctx, queryMergeParams, *queryMergeOutput); err != nil { os.Exit(checkError(err)) diff --git a/cmd/profilecli/tsdb.go b/cmd/profilecli/tsdb.go new file mode 100644 index 0000000000..4d12ec4649 --- /dev/null +++ b/cmd/profilecli/tsdb.go @@ -0,0 +1,64 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/prometheus/prometheus/model/labels" + + phlaremodel "github.com/grafana/pyroscope/pkg/model" + "github.com/grafana/pyroscope/pkg/phlaredb" + "github.com/grafana/pyroscope/pkg/phlaredb/tsdb/index" +) + +func tsdbSeries(ctx context.Context, path string) error { + r, err := index.NewFileReader(path) + if err != nil { + return err + } + + it, err := phlaredb.PostingsForMatchers(r, nil, labels.MustNewMatcher(labels.MatchNotEqual, "__name__", "")) + if err != nil { + return err + } + + var ( + lbls phlaremodel.Labels + chunkMeta []index.ChunkMeta + ) + line := struct { + SeriesRef uint64 + SeriesIndex *uint32 + Labels json.RawMessage + }{} + enc := json.NewEncoder(output(ctx)) + + for it.Next() { + if ctx.Err() != nil { + return ctx.Err() + } + + _, err = r.Series(it.At(), &lbls, &chunkMeta) + if err != nil { + return fmt.Errorf("error retrieving seriesRef: %w", err) + } + + line.Labels, err = lbls.ToPrometheusLabels().MarshalJSON() + if err != nil { + return fmt.Errorf("error marshalling labels: %w", err) + } + + if len(chunkMeta) > 0 { + line.SeriesIndex = &chunkMeta[0].SeriesIndex + } else { + line.SeriesIndex = nil + } + line.SeriesRef = uint64(it.At()) + if err := enc.Encode(&line); err != nil { + return fmt.Errorf("error writing line: %w", err) + } + } + + return nil +}