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(¶ms.BucketName) 45 queryCmd.Flag("object-store-type", "The type of the object storage (e.g., gcs).").Default("gcs").StringVar(¶ms.ObjectStoreType) 46 queryCmd.Flag("block", "Block ids to query on (accepts multiples)").StringsVar(¶ms.BlockIds) 47 queryCmd.Flag("tenant-id", "Tenant id of the queried block for remote bucket").StringVar(¶ms.TenantID) 48 queryCmd.Flag("query", "Label selector to query.").Default("{}").StringVar(¶ms.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(¶ms.Output) 56 queryCmd.Flag("profile-type", "Profile type to query.").Default("process_cpu:cpu:nanoseconds:cpu:nanoseconds").StringVar(¶ms.ProfileType) 57 queryCmd.Flag("stacktrace-selector", "Only query locations with those symbols. Provide multiple times starting with the root").StringsVar(¶ms.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(¶ms.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 }