github.com/grafana/pyroscope@v1.18.0/cmd/profilecli/query-blocks.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math"
     7  
     8  	"connectrpc.com/connect"
     9  	"github.com/go-kit/log/level"
    10  	"github.com/pkg/errors"
    11  
    12  	ingestv1 "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1"
    13  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    14  	"github.com/grafana/pyroscope/pkg/model"
    15  	"github.com/grafana/pyroscope/pkg/objstore"
    16  	objstoreclient "github.com/grafana/pyroscope/pkg/objstore/client"
    17  	"github.com/grafana/pyroscope/pkg/objstore/providers/filesystem"
    18  	"github.com/grafana/pyroscope/pkg/objstore/providers/gcs"
    19  	"github.com/grafana/pyroscope/pkg/phlaredb"
    20  )
    21  
    22  type blocksQueryParams struct {
    23  	BucketName      string
    24  	BlockIds        []string
    25  	TenantID        string
    26  	ObjectStoreType string
    27  	Query           string
    28  }
    29  
    30  type blocksQueryProfileParams struct {
    31  	*blocksQueryParams
    32  	Output             string
    33  	ProfileType        string
    34  	StacktraceSelector []string
    35  }
    36  
    37  type blocksQuerySeriesParams struct {
    38  	*blocksQueryParams
    39  	LabelNames []string
    40  }
    41  
    42  func addBlocksQueryParams(queryCmd commander) *blocksQueryParams {
    43  	params := new(blocksQueryParams)
    44  	queryCmd.Flag("bucket-name", "The name of the object storage bucket.").StringVar(&params.BucketName)
    45  	queryCmd.Flag("object-store-type", "The type of the object storage (e.g., gcs).").Default("gcs").StringVar(&params.ObjectStoreType)
    46  	queryCmd.Flag("block", "Block ids to query on (accepts multiples)").StringsVar(&params.BlockIds)
    47  	queryCmd.Flag("tenant-id", "Tenant id of the queried block for remote bucket").StringVar(&params.TenantID)
    48  	queryCmd.Flag("query", "Label selector to query.").Default("{}").StringVar(&params.Query)
    49  	return params
    50  }
    51  
    52  func addBlocksQueryProfileParams(queryCmd commander) *blocksQueryProfileParams {
    53  	params := new(blocksQueryProfileParams)
    54  	params.blocksQueryParams = addBlocksQueryParams(queryCmd)
    55  	queryCmd.Flag("output", "How to output the result, examples: console, raw, pprof=./my.pprof").Default("console").StringVar(&params.Output)
    56  	queryCmd.Flag("profile-type", "Profile type to query.").Default("process_cpu:cpu:nanoseconds:cpu:nanoseconds").StringVar(&params.ProfileType)
    57  	queryCmd.Flag("stacktrace-selector", "Only query locations with those symbols. Provide multiple times starting with the root").StringsVar(&params.StacktraceSelector)
    58  	return params
    59  }
    60  
    61  func addBlocksQuerySeriesParams(queryCmd commander) *blocksQuerySeriesParams {
    62  	params := new(blocksQuerySeriesParams)
    63  	params.blocksQueryParams = addBlocksQueryParams(queryCmd)
    64  	queryCmd.Flag("label-names", "Filter returned labels to the supplied label names. Without any filter all labels are returned.").StringsVar(&params.LabelNames)
    65  	return params
    66  }
    67  
    68  func blocksQueryProfile(ctx context.Context, params *blocksQueryProfileParams) error {
    69  	level.Info(logger).Log("msg", "blocks query profile", "blockIds", fmt.Sprintf("%v", params.BlockIds), "path",
    70  		cfg.blocks.path, "bucketName", params.BucketName, "tenantId", params.TenantID, "query", params.Query, "type", params.ProfileType)
    71  
    72  	if len(params.BlockIds) > 1 {
    73  		return errors.New("query profile is limited to a single block")
    74  	}
    75  
    76  	profileType, err := model.ParseProfileTypeSelector(params.ProfileType)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	var stackTraceSelectors *typesv1.StackTraceSelector = nil
    82  	if len(params.StacktraceSelector) > 0 {
    83  		locations := make([]*typesv1.Location, 0, len(params.StacktraceSelector))
    84  		for _, cs := range params.StacktraceSelector {
    85  			locations = append(locations, &typesv1.Location{
    86  				Name: cs,
    87  			})
    88  		}
    89  		stackTraceSelectors = &typesv1.StackTraceSelector{
    90  			CallSite: locations,
    91  		}
    92  		level.Info(logger).Log("msg", "selecting with stackstrace selector", "call-site", fmt.Sprintf("%#+v", params.StacktraceSelector))
    93  	}
    94  
    95  	bucket, err := getBucket(ctx, params.blocksQueryParams)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	meta, err := phlaredb.NewBlockQuerier(ctx, bucket).BlockMeta(ctx, params.BlockIds[0])
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	resp, err := phlaredb.NewSingleBlockQuerierFromMeta(ctx, bucket, meta).SelectMergePprof(
   106  		ctx,
   107  		&ingestv1.SelectProfilesRequest{
   108  			LabelSelector: params.Query,
   109  			Type:          profileType,
   110  			Start:         meta.MinTime.Time().UnixMilli(),
   111  			End:           meta.MaxTime.Time().UnixMilli(),
   112  		},
   113  		0,
   114  		stackTraceSelectors,
   115  	)
   116  	if err != nil {
   117  		return errors.Wrap(err, "failed to query")
   118  	}
   119  
   120  	return outputMergeProfile(ctx, params.Output, resp)
   121  }
   122  
   123  func blocksQuerySeries(ctx context.Context, params *blocksQuerySeriesParams) error {
   124  	level.Info(logger).Log("msg", "blocks query series", "labelNames", fmt.Sprintf("%v", params.LabelNames),
   125  		"blockIds", fmt.Sprintf("%v", params.BlockIds), "path", cfg.blocks.path, "bucketName", params.BucketName, "tenantId", params.TenantID)
   126  
   127  	bucket, err := getBucket(ctx, params.blocksQueryParams)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	blockQuerier := phlaredb.NewBlockQuerier(ctx, bucket)
   133  
   134  	var from, to int64
   135  	from, to = math.MaxInt64, math.MinInt64
   136  	var targetBlockQueriers phlaredb.Queriers
   137  	for _, blockId := range params.BlockIds {
   138  		meta, err := blockQuerier.BlockMeta(ctx, blockId)
   139  		if err != nil {
   140  			return err
   141  		}
   142  		from = min(from, meta.MinTime.Time().UnixMilli())
   143  		to = max(to, meta.MaxTime.Time().UnixMilli())
   144  		targetBlockQueriers = append(targetBlockQueriers, phlaredb.NewSingleBlockQuerierFromMeta(ctx, bucket, meta))
   145  	}
   146  
   147  	response, err := targetBlockQueriers.Series(ctx, connect.NewRequest(
   148  		&ingestv1.SeriesRequest{
   149  			Start:      from,
   150  			End:        to,
   151  			Matchers:   []string{params.Query},
   152  			LabelNames: params.LabelNames,
   153  		},
   154  	))
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	return outputSeries(response.Msg.LabelsSet)
   160  }
   161  
   162  func getBucket(ctx context.Context, params *blocksQueryParams) (objstore.Bucket, error) {
   163  	if params.BucketName != "" {
   164  		return getRemoteBucket(ctx, params)
   165  	} else {
   166  		return filesystem.NewBucket(cfg.blocks.path)
   167  	}
   168  }
   169  
   170  func getRemoteBucket(ctx context.Context, params *blocksQueryParams) (objstore.Bucket, error) {
   171  	if params.TenantID == "" {
   172  		return nil, errors.New("specify tenant id for remote bucket")
   173  	}
   174  	return objstoreclient.NewBucket(ctx, objstoreclient.Config{
   175  		StorageBackendConfig: objstoreclient.StorageBackendConfig{
   176  			Backend: params.ObjectStoreType,
   177  			GCS: gcs.Config{
   178  				BucketName: params.BucketName,
   179  			},
   180  		},
   181  		Prefix: fmt.Sprintf("%s/phlaredb", params.TenantID),
   182  	}, params.BucketName)
   183  }