Descargar datos de perfil

En este documento se describe cómo puedes descargar los datos de tu perfil en tu sistema local y cómo puedes obtenerlos de forma programática mediante una aplicación Go.

Descargar perfiles mediante la Google Cloud consola

Para descargar el perfil que se muestra en el gráfico de llamas, haz clic en Descargar .

Profiler usa la siguiente convención de nomenclatura para el archivo descargado:

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

En esta expresión:

  • SERVICE_NAME contiene la selección de Servicio
  • PROFILE_TYPE contiene la selección de Tipo de perfil.
  • FROM_DATE y TO_DATE contienen las especificaciones del periodo
  • ZONE contiene la selección de Zona.
  • VERSION contiene la selección de Versión.

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

Descargar perfiles mediante programación

Para obtener datos de perfil, usa el método de la API ListProfiles. En el siguiente programa de Go de ejemplo se muestra cómo usar esta API.

El programa de ejemplo crea una carpeta en el directorio desde el que se ejecuta y genera un conjunto de archivos pprof numerados. Cada archivo tiene una convención de nomenclatura similar a profile000042.pb.gz. Cada directorio contiene datos de perfil y un archivo de metadatos (metadata.csv), que incluye información sobre los archivos descargados.


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

El programa de ejemplo acepta los siguientes argumentos de línea de comandos:

  • project: el proyecto del que se obtienen los perfiles. Obligatorio.
  • page_size: número máximo de perfiles recuperados por llamada a la API. El valor máximo de page_size es 1000. Si no se especifica, este campo tiene el valor 100.
  • page_token: token de cadena generado por una ejecución anterior del programa para reanudar las descargas. Opcional.
  • max_profiles: número máximo de perfiles que se van a obtener. Si se proporciona un número entero no positivo, el programa intentará recuperar todos los perfiles.
    Opcional.

Ejecutar la aplicación de ejemplo

Para ejecutar la aplicación de ejemplo, haz lo siguiente:

  1. Clona el repositorio:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
    
  2. Cambia al directorio que contiene el programa de ejemplo:

    cd golang-samples/profiler/export
    
  3. Ejecuta el programa después de sustituir YOUR_GCP_PROJECT por el ID de tu proyectoGoogle Cloud :

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

El programa puede tardar bastante en completarse. El programa genera un token para la página siguiente después de recuperar la página actual. Puedes usar el token para reanudar el proceso si el programa se interrumpe.

Ver los perfiles descargados

Para leer un archivo descargado, que está escrito en formato de búfer de protocolo serializado, usa la herramienta de código abierto pprof.