Profildaten herunterladen

In diesem Dokument wird beschrieben, wie Sie Ihre Profildaten auf Ihr lokales System herunterladen und wie Sie Profildaten programmatisch mithilfe einer Go-Anwendung abrufen können.

Profile mit der Google Cloud Console herunterladen

Wenn Sie das im Flame-Diagramm angezeigte Profil herunterladen möchten, klicken Sie auf Herunterladen .

Profiler verwendet für die heruntergeladene Datei die folgende Namenskonvention:

profiler_[SERVICE_NAME]_[PROFILE_TYPE]_[FROM_DATE]_[TO_DATE]_[ZONE]_[VERSION].pb.gz

Beispielausdruck:

  • SERVICE_NAME enthält Ihre Auswahl für den Service (Dienst).
  • PROFILE_TYPE enthält Ihre Auswahl für den Profile type (Profiltyp).
  • FROM_DATE und TO_DATE enthalten Ihre Zeitraumangaben.
  • ZONE enthält Ihre Auswahl für Zone.
  • VERSION enthält Ihre Auswahl für Version.

Beispiel: profiler_docdemo-service_HEAP_2018-04-22T20_25_31Z_2018-05-22T20_25_31Z_us-east1-c.pb.gz

Profile programmatisch herunterladen

Verwenden Sie die API-Methode ListProfiles, um Profildaten abzurufen. Das folgende Beispielprogramm in Go veranschaulicht die Verwendung dieser API.

Das Beispielprogramm erstellt einen Ordner im Verzeichnis, aus dem es ausgeführt wird, und generiert eine Reihe nummerierter pprof-Dateien. Jede Datei hat eine Benennungskonvention, die profile000042.pb.gz ähnelt. Jedes Verzeichnis enthält Profildaten und eine Metadatendatei (metadata.csv), die Informationen zu den heruntergeladenen Dateien enthält.


// Sample export shows how ListProfiles API can be used to download
// existing pprof profiles for a given project from GCP.
package main

import (
	"bytes"
	"context"
	"encoding/csv"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	cloudprofiler "cloud.google.com/go/cloudprofiler/apiv2"
	pb "cloud.google.com/go/cloudprofiler/apiv2/cloudprofilerpb"
	"google.golang.org/api/iterator"
)

var project = flag.String("project", "", "GCP project ID from which profiles should be fetched")
var pageSize = flag.Int("page_size", 100, "Number of profiles fetched per page. Maximum 1000.")
var pageToken = flag.String("page_token", "", "PageToken from a previous ListProfiles call. If empty, the listing will start from the begnning. Invalid page tokens result in error.")
var maxProfiles = flag.Int("max_profiles", 1000, "Maximum number of profiles to fetch across all pages. If this is <= 0, will fetch all available profiles")

const ProfilesDownloadedSuccessfully = "Read max allowed profiles"

// This function reads profiles for a given project and stores them into locally created files.
// The profile metadata gets stored into a 'metdata.csv' file, while the individual pprof files
// are created per profile.
func downloadProfiles(ctx context.Context, w io.Writer, project, pageToken string, pageSize, maxProfiles int) error {
	client, err := cloudprofiler.NewExportClient(ctx)
	if err != nil {
		return err
	}
	defer client.Close()
	log.Printf("Attempting to fetch %v profiles with a pageSize of %v for %v\n", maxProfiles, pageSize, project)

	// Initial request for the ListProfiles API
	request := &pb.ListProfilesRequest{
		Parent:    fmt.Sprintf("projects/%s", project),
		PageSize:  int32(pageSize),
		PageToken: pageToken,
	}

	// create a folder for storing profiles & metadata
	profilesDirName := fmt.Sprintf("profiles_%v", time.Now().Unix())
	if err := os.Mkdir(profilesDirName, 0750); err != nil {
		log.Fatal(err)
	}
	// create a file for storing profile metadata
	metadata, err := os.Create(fmt.Sprintf("%s/metadata.csv", profilesDirName))
	if err != nil {
		return err
	}
	defer metadata.Close()

	writer := csv.NewWriter(metadata)
	defer writer.Flush()

	writer.Write([]string{"File", "Name", "ProfileType", "Target", "Duration", "Labels"})

	profileCount := 0
	// Keep calling ListProfiles API till all profile pages are fetched or max pages reached
	profilesIterator := client.ListProfiles(ctx, request)
	for {
		// Read individual profile - the client will automatically make API calls to fetch next pages
		profile, err := profilesIterator.Next()

		if err == iterator.Done {
			log.Println("Read all available profiles")
			break
		}
		if err != nil {
			return fmt.Errorf("error reading profile from response: %w", err)
		}
		profileCount++

		filename := fmt.Sprintf("%s/profile%06d.pb.gz", profilesDirName, profileCount)
		err = os.WriteFile(filename, profile.ProfileBytes, 0640)

		if err != nil {
			return fmt.Errorf("unable to write file %s: %w", filename, err)
		}
		fmt.Fprintf(w, "deployment target: %v\n", profile.Deployment.Labels)

		labelBytes, err := json.Marshal(profile.Labels)
		if err != nil {
			return err
		}

		err = writer.Write([]string{filename, profile.Name, profile.Deployment.Target, profile.Duration.String(), string(labelBytes)})
		if err != nil {
			return err
		}

		if maxProfiles > 0 && profileCount >= maxProfiles {
			fmt.Fprintf(w, "result: %v", ProfilesDownloadedSuccessfully)
			break
		}

		if profilesIterator.PageInfo().Remaining() == 0 {
			// This signifies that the client will make a new API call internally
			log.Printf("next page token: %v\n", profilesIterator.PageInfo().Token)
		}
	}
	return nil
}

func main() {
	flag.Parse()
	// validate project ID
	if *project == "" {
		log.Fatalf("No project ID provided, please provide the GCP project ID via '-project' flag")
	}
	var writer bytes.Buffer
	if err := downloadProfiles(context.Background(), &writer, *project, *pageToken, *pageSize, *maxProfiles); err != nil {
		log.Fatal(err)
	}
	log.Println("Finished reading all profiles")
}

Das Beispielprogramm akzeptiert die folgenden Befehlszeilenargumente:

  • project: Das Projekt, aus dem die Profile abgerufen werden. Erforderlich.
  • page_size: Die maximale Anzahl von Profilen, die pro API-Aufruf abgerufen werden. Der Maximalwert von page_size ist 1.000. Wenn dieses Feld nicht angegeben ist, wird es auf „100“ gesetzt.
  • page_token: Ein String-Token, das bei einem vorherigen Durchlauf des Programms generiert wurde, um Downloads fortzusetzen. Optional.
  • max_profiles: Die maximale Anzahl der Profile, die abgerufen werden sollen. Wenn eine nicht positive Ganzzahl angegeben wird, versucht das Programm, alle Profile abzurufen.
    Optional.

Beispielanwendung ausführen

So führen Sie die Beispielanwendung aus:

  1. Klonen Sie das Repository:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
    
  2. Wechseln Sie zu dem Verzeichnis, das das Beispielprogramm enthält:

    cd golang-samples/profiler/export
    
  3. Führen Sie das Programm aus, nachdem Sie YOUR_GCP_PROJECT durch die ID Ihres Google Cloud-Projekts ersetzt haben:

    go run main.go -project YOUR_GCP_PROJECT -page_size 1000 -max_profiles 10000
    

Das Programm kann ziemlich lange dauern. Das Programm gibt nach dem Abrufen der aktuellen Seite ein Token für die nächste Seite aus. Mit dem Token können Sie den Vorgang fortsetzen, wenn das Programm unterbrochen wird.

Heruntergeladene Profile ansehen

Die heruntergeladene Datei ist im Format des serialisierten Protokollpuffers. Verwenden Sie das Open-Source-Tool pprof, um sie zu lesen.