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 zum Abrufen von Profildaten die API-Methode ListProfiles. Im folgenden Go-Beispielprogramm wird die Verwendung dieser API veranschaulicht.

Das Beispielprogramm erstellt einen Ordner in dem Verzeichnis, in dem es ausgeführt wird, und generiert eine Reihe nummerierter Dateien vom Typ pprof. Jede Datei hat eine ähnliche Namenskonvention wie profile000042.pb.gz. Jedes Verzeichnis enthält Profildaten und die Metadatendatei metadata.csv mit Informationen zu den heruntergeladenen Dateien.


// 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: %v", 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: %v", 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 Höchstwert von page_size beträgt 1.000. Wenn keine Angabe erfolgt, wird dieses Feld auf 100 gesetzt.
  • page_token: Ein Stringtoken, das bei einer vorherigen Ausführung des Programms generiert wurde, um Downloads fortzusetzen. Optional.
  • max_profiles: Die maximale Anzahl von Profilen, 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 in das 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
    

Die Teilnahme am Programm kann einige Zeit in Anspruch nehmen. Nach dem Abrufen der aktuellen Seite gibt das Programm ein Token für die nächste Seite aus. Sie können das Token verwenden, um den Prozess fortzusetzen, wenn das Programm unterbrochen wird.

Heruntergeladene Profile ansehen

Verwenden Sie das Open-Source-Tool pprof, um eine heruntergeladene Datei zu lesen, die im serialisierten Protokollpufferformat geschrieben ist.