Télécharger les données du profil

Ce document explique comment télécharger vos données de profil sur votre système local et comment les récupérer de manière programmatique à l'aide d'une application Go.

Télécharger des profils à l'aide de la console Google Cloud

Pour télécharger le profil affiché dans le graphique de type "flamme", cliquez sur Télécharger .

Profiler utilise la convention d'attribution de noms suivante pour le fichier téléchargé :

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

Dans cette expression :

  • SERVICE_NAME correspond au service sélectionné ;
  • PROFILE_TYPE contient le type de profil sélectionné ;
  • FROM_DATE et TO_DATE sont remplacés par les informations de la période d'affichage spécifiées ;
  • ZONE contient la zone sélectionnée ;
  • VERSION correspond à la version sélectionnée.

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

Télécharger des profils par programmation

Pour récupérer des données de profil, utilisez la méthode API ListProfiles. L'exemple de programme Go suivant illustre l'utilisation de cette API.

L'exemple de programme crée un dossier dans le répertoire à partir duquel il est exécuté et génère un ensemble de fichiers pprof numérotés. Chaque fichier a une convention de dénomination semblable à profile000042.pb.gz. Chaque répertoire contient des données de profil et un fichier de métadonnées (metadata.csv), qui contient des informations sur les fichiers téléchargés.


// 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")
}

L'exemple de programme accepte les arguments de ligne de commande suivants:

  • project: projet à partir duquel les profils sont récupérés. Obligatoire.
  • page_size: nombre maximal de profils récupérés par appel d'API. La valeur maximale de page_size est 1 000. Si aucune valeur n'est spécifiée, ce champ est défini sur 100.
  • page_token: jeton de chaîne généré par une exécution précédente du programme pour reprendre les téléchargements. Facultatif.
  • max_profiles: nombre maximal de profils à récupérer. Si un entier non positif est fourni, le programme tente de récupérer tous les profils.
    Facultatif.

Exécuter l'exemple d'application

Pour exécuter l'exemple d'application, procédez comme suit:

  1. Clonez le dépôt :

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
    
  2. Accédez au répertoire qui contient l'exemple de programme:

    cd golang-samples/profiler/export
    
  3. Exécutez le programme après avoir remplacé YOUR_GCP_PROJECT par l'ID de votre projet Google Cloud:

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

L'exécution du programme peut prendre un certain temps. Le programme génère un jeton pour la page suivante après avoir récupéré la page actuelle. Vous pouvez utiliser le jeton pour reprendre le processus si le programme est interrompu.

Afficher les profils téléchargés

Pour lire un fichier téléchargé, qui est écrit au format tampon de protocole sérialisé, utilisez l'outil Open Source pprof.