Source file src/cmd/internal/pgo/deserialize.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pgo
     6  
     7  import (
     8  	"bufio"
     9  	"fmt"
    10  	"io"
    11  	"strings"
    12  	"strconv"
    13  )
    14  
    15  // IsSerialized returns true if r is a serialized Profile.
    16  //
    17  // IsSerialized only peeks at r, so seeking back after calling is not
    18  // necessary.
    19  func IsSerialized(r *bufio.Reader) (bool, error) {
    20  	hdr, err := r.Peek(len(serializationHeader))
    21  	if err == io.EOF {
    22  		// Empty file.
    23  		return false, nil
    24  	} else if err != nil {
    25  		return false, fmt.Errorf("error reading profile header: %w", err)
    26  	}
    27  
    28  	return string(hdr) == serializationHeader, nil
    29  }
    30  
    31  // FromSerialized parses a profile from serialization output of Profile.WriteTo.
    32  func FromSerialized(r io.Reader) (*Profile, error) {
    33  	d := emptyProfile()
    34  
    35  	scanner := bufio.NewScanner(r)
    36  	scanner.Split(bufio.ScanLines)
    37  
    38  	if !scanner.Scan() {
    39  		if err := scanner.Err(); err != nil {
    40  			return nil, fmt.Errorf("error reading preprocessed profile: %w", err)
    41  		}
    42  		return nil, fmt.Errorf("preprocessed profile missing header")
    43  	}
    44  	if gotHdr := scanner.Text() + "\n"; gotHdr != serializationHeader {
    45  		return nil, fmt.Errorf("preprocessed profile malformed header; got %q want %q", gotHdr, serializationHeader)
    46  	}
    47  
    48  	for scanner.Scan() {
    49  		readStr := scanner.Text()
    50  
    51  		callerName := readStr
    52  
    53  		if !scanner.Scan() {
    54  			if err := scanner.Err(); err != nil {
    55  				return nil, fmt.Errorf("error reading preprocessed profile: %w", err)
    56  			}
    57  			return nil, fmt.Errorf("preprocessed profile entry missing callee")
    58  		}
    59  		calleeName := scanner.Text()
    60  
    61  		if !scanner.Scan() {
    62  			if err := scanner.Err(); err != nil {
    63  				return nil, fmt.Errorf("error reading preprocessed profile: %w", err)
    64  			}
    65  			return nil, fmt.Errorf("preprocessed profile entry missing weight")
    66  		}
    67  		readStr = scanner.Text()
    68  
    69  		split := strings.Split(readStr, " ")
    70  
    71  		if len(split) != 2 {
    72  			return nil, fmt.Errorf("preprocessed profile entry got %v want 2 fields", split)
    73  		}
    74  
    75  		co, err := strconv.Atoi(split[0])
    76  		if err != nil {
    77  			return nil, fmt.Errorf("preprocessed profile error processing call line: %w", err)
    78  		}
    79  
    80  		edge := NamedCallEdge{
    81  			CallerName:     callerName,
    82  			CalleeName:     calleeName,
    83  			CallSiteOffset: co,
    84  		}
    85  
    86  		weight, err := strconv.ParseInt(split[1], 10, 64)
    87  		if err != nil {
    88  			return nil, fmt.Errorf("preprocessed profile error processing call weight: %w", err)
    89  		}
    90  
    91  		if _, ok := d.NamedEdgeMap.Weight[edge]; ok {
    92  			return nil, fmt.Errorf("preprocessed profile contains duplicate edge %+v", edge)
    93  		}
    94  
    95  		d.NamedEdgeMap.ByWeight = append(d.NamedEdgeMap.ByWeight, edge) // N.B. serialization is ordered.
    96  		d.NamedEdgeMap.Weight[edge] += weight
    97  		d.TotalWeight += weight
    98  	}
    99  
   100  	return d, nil
   101  
   102  }
   103  

View as plain text