build/perfdata/client.go

293 строки
7.6 KiB
Go
Исходник Обычный вид История

perf,perfdata: copy perf[data].golang.org code from x/perf This change re-homes the code for perf.golang.org and perfdata.golang.org (available in x/perf/analysis and x/perf/storage respectively) to x/build to avoid any issues with backwards compatibility. We're much more lax about the Go 1 compatibility promise in x/build than in other x/ repos (except maybe x/exp) so we can make bigger changes. Also, we may re-home these services to live closer to the rest of the build infrastructure, so co-locating the code also makes sense. The code was taken from golang.org/x/perf (or go.googlesource.com/perf) at git hash 64dc439b20aef7259bd73a5452063060adb8d898, or CL 392658. In order to make this work, we also copy the internal diff and basedir packages from x/perf. There are a few things these depend on that we have to leave behind. One is x/perf/benchstat, which means we also leave behind x/perf/storage/benchfmt, which it depends on. Both are OK because they have new and shiny replacements that we'd rather use anyway. This change also involved running a bunch of sed commands to update package import paths as well as comments. The full diff is included in the commit for review, but will be removed before landing. For golang/go#48803. Change-Id: Ib15840c15254bc8bfa266bbc82e1df7cf4c252db Reviewed-on: https://go-review.googlesource.com/c/build/+/395175 Reviewed-by: Michael Pratt <mpratt@google.com> Trust: Dmitri Shuralyov <dmitshur@golang.org> Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-24 01:27:41 +03:00
// Copyright 2017 The Go 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 perfdata contains a client for the performance data storage server.
package perfdata
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"golang.org/x/perf/storage/benchfmt"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)
// A Client issues queries to a performance data storage server.
// It is safe to use from multiple goroutines simultaneously.
type Client struct {
// BaseURL is the base URL of the storage server.
BaseURL string
// HTTPClient is the HTTP client for sending requests. If nil, http.DefaultClient will be used.
HTTPClient *http.Client
}
// httpClient returns the http.Client to use for requests.
func (c *Client) httpClient() *http.Client {
if c.HTTPClient != nil {
return c.HTTPClient
}
return http.DefaultClient
}
// Query searches for results matching the given query string. The
// result is a stream of bytes containing text benchmark data. This data
// may be parsed and processed by the x/perf/benchfmt package.
perf,perfdata: copy perf[data].golang.org code from x/perf This change re-homes the code for perf.golang.org and perfdata.golang.org (available in x/perf/analysis and x/perf/storage respectively) to x/build to avoid any issues with backwards compatibility. We're much more lax about the Go 1 compatibility promise in x/build than in other x/ repos (except maybe x/exp) so we can make bigger changes. Also, we may re-home these services to live closer to the rest of the build infrastructure, so co-locating the code also makes sense. The code was taken from golang.org/x/perf (or go.googlesource.com/perf) at git hash 64dc439b20aef7259bd73a5452063060adb8d898, or CL 392658. In order to make this work, we also copy the internal diff and basedir packages from x/perf. There are a few things these depend on that we have to leave behind. One is x/perf/benchstat, which means we also leave behind x/perf/storage/benchfmt, which it depends on. Both are OK because they have new and shiny replacements that we'd rather use anyway. This change also involved running a bunch of sed commands to update package import paths as well as comments. The full diff is included in the commit for review, but will be removed before landing. For golang/go#48803. Change-Id: Ib15840c15254bc8bfa266bbc82e1df7cf4c252db Reviewed-on: https://go-review.googlesource.com/c/build/+/395175 Reviewed-by: Michael Pratt <mpratt@google.com> Trust: Dmitri Shuralyov <dmitshur@golang.org> Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-24 01:27:41 +03:00
//
// The query string is first parsed into quoted words (as in the shell)
// and then each word must be formatted as one of the following:
// key:value - exact match on label "key" = "value"
// key>value - value greater than (useful for dates)
// key<value - value less than (also useful for dates)
func (c *Client) Query(ctx context.Context, q string) (io.ReadCloser, error) {
perf,perfdata: copy perf[data].golang.org code from x/perf This change re-homes the code for perf.golang.org and perfdata.golang.org (available in x/perf/analysis and x/perf/storage respectively) to x/build to avoid any issues with backwards compatibility. We're much more lax about the Go 1 compatibility promise in x/build than in other x/ repos (except maybe x/exp) so we can make bigger changes. Also, we may re-home these services to live closer to the rest of the build infrastructure, so co-locating the code also makes sense. The code was taken from golang.org/x/perf (or go.googlesource.com/perf) at git hash 64dc439b20aef7259bd73a5452063060adb8d898, or CL 392658. In order to make this work, we also copy the internal diff and basedir packages from x/perf. There are a few things these depend on that we have to leave behind. One is x/perf/benchstat, which means we also leave behind x/perf/storage/benchfmt, which it depends on. Both are OK because they have new and shiny replacements that we'd rather use anyway. This change also involved running a bunch of sed commands to update package import paths as well as comments. The full diff is included in the commit for review, but will be removed before landing. For golang/go#48803. Change-Id: Ib15840c15254bc8bfa266bbc82e1df7cf4c252db Reviewed-on: https://go-review.googlesource.com/c/build/+/395175 Reviewed-by: Michael Pratt <mpratt@google.com> Trust: Dmitri Shuralyov <dmitshur@golang.org> Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-24 01:27:41 +03:00
hc := c.httpClient()
resp, err := ctxhttp.Get(ctx, hc, c.BaseURL+"/search?"+url.Values{"q": []string{q}}.Encode())
if err != nil {
return nil, err
perf,perfdata: copy perf[data].golang.org code from x/perf This change re-homes the code for perf.golang.org and perfdata.golang.org (available in x/perf/analysis and x/perf/storage respectively) to x/build to avoid any issues with backwards compatibility. We're much more lax about the Go 1 compatibility promise in x/build than in other x/ repos (except maybe x/exp) so we can make bigger changes. Also, we may re-home these services to live closer to the rest of the build infrastructure, so co-locating the code also makes sense. The code was taken from golang.org/x/perf (or go.googlesource.com/perf) at git hash 64dc439b20aef7259bd73a5452063060adb8d898, or CL 392658. In order to make this work, we also copy the internal diff and basedir packages from x/perf. There are a few things these depend on that we have to leave behind. One is x/perf/benchstat, which means we also leave behind x/perf/storage/benchfmt, which it depends on. Both are OK because they have new and shiny replacements that we'd rather use anyway. This change also involved running a bunch of sed commands to update package import paths as well as comments. The full diff is included in the commit for review, but will be removed before landing. For golang/go#48803. Change-Id: Ib15840c15254bc8bfa266bbc82e1df7cf4c252db Reviewed-on: https://go-review.googlesource.com/c/build/+/395175 Reviewed-by: Michael Pratt <mpratt@google.com> Trust: Dmitri Shuralyov <dmitshur@golang.org> Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-24 01:27:41 +03:00
}
if resp.StatusCode != 200 {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
perf,perfdata: copy perf[data].golang.org code from x/perf This change re-homes the code for perf.golang.org and perfdata.golang.org (available in x/perf/analysis and x/perf/storage respectively) to x/build to avoid any issues with backwards compatibility. We're much more lax about the Go 1 compatibility promise in x/build than in other x/ repos (except maybe x/exp) so we can make bigger changes. Also, we may re-home these services to live closer to the rest of the build infrastructure, so co-locating the code also makes sense. The code was taken from golang.org/x/perf (or go.googlesource.com/perf) at git hash 64dc439b20aef7259bd73a5452063060adb8d898, or CL 392658. In order to make this work, we also copy the internal diff and basedir packages from x/perf. There are a few things these depend on that we have to leave behind. One is x/perf/benchstat, which means we also leave behind x/perf/storage/benchfmt, which it depends on. Both are OK because they have new and shiny replacements that we'd rather use anyway. This change also involved running a bunch of sed commands to update package import paths as well as comments. The full diff is included in the commit for review, but will be removed before landing. For golang/go#48803. Change-Id: Ib15840c15254bc8bfa266bbc82e1df7cf4c252db Reviewed-on: https://go-review.googlesource.com/c/build/+/395175 Reviewed-by: Michael Pratt <mpratt@google.com> Trust: Dmitri Shuralyov <dmitshur@golang.org> Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-24 01:27:41 +03:00
}
return nil, fmt.Errorf("%s", body)
perf,perfdata: copy perf[data].golang.org code from x/perf This change re-homes the code for perf.golang.org and perfdata.golang.org (available in x/perf/analysis and x/perf/storage respectively) to x/build to avoid any issues with backwards compatibility. We're much more lax about the Go 1 compatibility promise in x/build than in other x/ repos (except maybe x/exp) so we can make bigger changes. Also, we may re-home these services to live closer to the rest of the build infrastructure, so co-locating the code also makes sense. The code was taken from golang.org/x/perf (or go.googlesource.com/perf) at git hash 64dc439b20aef7259bd73a5452063060adb8d898, or CL 392658. In order to make this work, we also copy the internal diff and basedir packages from x/perf. There are a few things these depend on that we have to leave behind. One is x/perf/benchstat, which means we also leave behind x/perf/storage/benchfmt, which it depends on. Both are OK because they have new and shiny replacements that we'd rather use anyway. This change also involved running a bunch of sed commands to update package import paths as well as comments. The full diff is included in the commit for review, but will be removed before landing. For golang/go#48803. Change-Id: Ib15840c15254bc8bfa266bbc82e1df7cf4c252db Reviewed-on: https://go-review.googlesource.com/c/build/+/395175 Reviewed-by: Michael Pratt <mpratt@google.com> Trust: Dmitri Shuralyov <dmitshur@golang.org> Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-24 01:27:41 +03:00
}
return resp.Body, nil
perf,perfdata: copy perf[data].golang.org code from x/perf This change re-homes the code for perf.golang.org and perfdata.golang.org (available in x/perf/analysis and x/perf/storage respectively) to x/build to avoid any issues with backwards compatibility. We're much more lax about the Go 1 compatibility promise in x/build than in other x/ repos (except maybe x/exp) so we can make bigger changes. Also, we may re-home these services to live closer to the rest of the build infrastructure, so co-locating the code also makes sense. The code was taken from golang.org/x/perf (or go.googlesource.com/perf) at git hash 64dc439b20aef7259bd73a5452063060adb8d898, or CL 392658. In order to make this work, we also copy the internal diff and basedir packages from x/perf. There are a few things these depend on that we have to leave behind. One is x/perf/benchstat, which means we also leave behind x/perf/storage/benchfmt, which it depends on. Both are OK because they have new and shiny replacements that we'd rather use anyway. This change also involved running a bunch of sed commands to update package import paths as well as comments. The full diff is included in the commit for review, but will be removed before landing. For golang/go#48803. Change-Id: Ib15840c15254bc8bfa266bbc82e1df7cf4c252db Reviewed-on: https://go-review.googlesource.com/c/build/+/395175 Reviewed-by: Michael Pratt <mpratt@google.com> Trust: Dmitri Shuralyov <dmitshur@golang.org> Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-24 01:27:41 +03:00
}
// UploadInfo represents an upload summary.
type UploadInfo struct {
Count int
UploadID string
LabelValues benchfmt.Labels `json:",omitempty"`
}
// ListUploads searches for uploads containing results matching the given query string.
// The query may be empty, in which case all uploads will be returned.
// extraLabels specifies other labels to be retrieved.
// If limit is 0, no limit will be provided to the server.
// The uploads are returned starting with the most recent upload.
func (c *Client) ListUploads(ctx context.Context, q string, extraLabels []string, limit int) *UploadList {
hc := c.httpClient()
v := url.Values{"extra_label": extraLabels}
if q != "" {
v["q"] = []string{q}
}
if limit != 0 {
v["limit"] = []string{fmt.Sprintf("%d", limit)}
}
u := c.BaseURL + "/uploads"
if len(v) > 0 {
u += "?" + v.Encode()
}
resp, err := ctxhttp.Get(ctx, hc, u)
if err != nil {
return &UploadList{err: err}
}
if resp.StatusCode != 200 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return &UploadList{err: err}
}
return &UploadList{err: fmt.Errorf("%s", body)}
}
return &UploadList{body: resp.Body, dec: json.NewDecoder(resp.Body)}
}
// UploadList is the result of ListUploads.
// Use Next to advance through the rows, making sure to call Close when done:
//
// q := db.ListUploads("key:value")
// defer q.Close()
// for q.Next() {
// id, count := q.Row()
// labels := q.LabelValues()
// ...
// }
// err = q.Err() // get any error encountered during iteration
// ...
type UploadList struct {
body io.Closer
dec *json.Decoder
// from last call to Next
ui UploadInfo
err error
}
// Next prepares the next result for reading with the Result
// method. It returns false when there are no more results, either by
// reaching the end of the input or an error.
func (ul *UploadList) Next() bool {
if ul.err != nil {
return false
}
// Clear UploadInfo before decoding new value.
ul.ui = UploadInfo{}
ul.err = ul.dec.Decode(&ul.ui)
return ul.err == nil
}
// Info returns the most recent UploadInfo generated by a call to Next.
func (ul *UploadList) Info() UploadInfo {
return ul.ui
}
// Err returns the error state of the query.
func (ul *UploadList) Err() error {
if ul.err == io.EOF {
return nil
}
return ul.err
}
// Close frees resources associated with the query.
func (ul *UploadList) Close() error {
if ul.body != nil {
err := ul.body.Close()
ul.body = nil
return err
}
return ul.Err()
}
// NewUpload starts a new upload to the storage server.
// The upload must have Abort or Commit called on it.
// If the server requires authentication for uploads, c.HTTPClient should be set to the result of oauth2.NewClient.
func (c *Client) NewUpload(ctx context.Context) *Upload {
hc := c.httpClient()
pr, pw := io.Pipe()
mpw := multipart.NewWriter(pw)
req, err := http.NewRequest("POST", c.BaseURL+"/upload", pr)
if err != nil {
return &Upload{err: err}
}
req.Header.Set("Content-Type", mpw.FormDataContentType())
req.Header.Set("User-Agent", "golang.org/x/build/perfdata")
errCh := make(chan error)
u := &Upload{pw: pw, mpw: mpw, errCh: errCh}
go func() {
resp, err := ctxhttp.Do(ctx, hc, req)
if err != nil {
errCh <- err
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
body, _ := ioutil.ReadAll(resp.Body)
errCh <- fmt.Errorf("upload failed: %v\n%s", resp.Status, body)
return
}
status := &UploadStatus{}
if err := json.NewDecoder(resp.Body).Decode(status); err != nil {
errCh <- err
}
u.status = status
errCh <- nil
}()
return u
}
// UploadStatus contains information about a successful upload.
type UploadStatus struct {
// UploadID is the upload ID assigned to the upload.
UploadID string `json:"uploadid"`
// FileIDs is the list of file IDs assigned to the files in the upload.
FileIDs []string `json:"fileids"`
// ViewURL is a server-supplied URL to view the results.
ViewURL string `json:"viewurl"`
}
// An Upload is an in-progress upload.
// Use CreateFile to upload one or more files, then call Commit or Abort.
//
// u := client.NewUpload()
// w, err := u.CreateFile()
// if err != nil {
// u.Abort()
// return err
// }
// fmt.Fprintf(w, "BenchmarkResult 1 1 ns/op\n")
// if err := u.Commit(); err != nil {
// return err
// }
type Upload struct {
pw io.WriteCloser
mpw *multipart.Writer
status *UploadStatus
// errCh is used to report the success/failure of the HTTP request
errCh chan error
// err is the first observed error; it is only accessed from user-called methods for thread safety
err error
}
// CreateFile creates a new upload with the given name.
// The Writer may be used until CreateFile is called again.
// name may be the empty string if the file does not have a name.
func (u *Upload) CreateFile(name string) (io.Writer, error) {
if u.err != nil {
return nil, u.err
}
return u.mpw.CreateFormFile("file", name)
}
// Commit attempts to commit the upload.
func (u *Upload) Commit() (*UploadStatus, error) {
if u.err != nil {
return nil, u.err
}
if u.err = u.mpw.WriteField("commit", "1"); u.err != nil {
u.Abort()
return nil, u.err
}
if u.err = u.mpw.Close(); u.err != nil {
u.Abort()
return nil, u.err
}
u.mpw = nil
if u.err = u.pw.Close(); u.err != nil {
u.Abort()
return nil, u.err
}
u.pw = nil
u.err = <-u.errCh
u.errCh = nil
if u.err != nil {
return nil, u.err
}
return u.status, nil
}
// Abort attempts to cancel the in-progress upload.
func (u *Upload) Abort() error {
if u.mpw != nil {
u.mpw.WriteField("abort", "1")
// Writing the 'abort' field will cause the server to send back an error response.
u.mpw.Close()
u.mpw = nil
}
if u.pw != nil {
u.pw.Close()
u.pw = nil
}
err := <-u.errCh
u.errCh = nil
if u.err == nil {
u.err = err
}
return u.err
}