<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package perffile

import (
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"reflect"
	"sort"
)

// TODO: Type for file format errors.

// A File is a perf.data file. It consists of a sequence of records,
// which can be retrieved with the Records method, as well as several
// optional metadata fields.
type File struct {
	r      io.ReaderAt
	closer io.Closer
	hdr    fileHeader

	attrs    []fileAttr
	idToAttr map[attrID]*EventAttr

	featureSections map[feature]fileSection

	sampleIDOffset int // byte offset of AttrID in sample

	sampleIDAll    bool // non-samples have sample_id trailer
	recordIDOffset int  // byte offset of AttrID in non-sample, from end
}

// New reads a "perf.data" file from r.
//
// The caller must keep r open as long as it is using the returned
// *File.
func New(r io.ReaderAt) (*File, error) {
	file := &File{r: r}

	// See perf_session__read_header in tools/perf/util/header.c

	// Read header
	sr := io.NewSectionReader(r, 0, 1024)
	if err := binary.Read(sr, binary.LittleEndian, &file.hdr); err != nil {
		return nil, err
	}
	switch string(file.hdr.Magic[:]) {
	case "PERFILE2":
		// Version 2, little endian.
		break
	case "2ELIFREP":
		// Version 2, big endian.
		//
		// TODO: Support big endian profiles.
		return nil, fmt.Errorf("big endian profiles not supported")
	case "PERFFILE":
		// Version 1 file.
		return nil, fmt.Errorf("version 1 profiles not supported")
	default:
		return nil, fmt.Errorf("bad or unsupported file magic %q", string(file.hdr.Magic[:]))
	}
	if file.hdr.Size != uint64(binary.Size(&file.hdr)) {
		return nil, fmt.Errorf("bad header size %d", file.hdr.Size)
	}
	// TODO: perf supports attrSize 64, 72, 80, and 96 for compatibility
	if file.hdr.AttrSize != uint64(binary.Size(&fileAttr{})) {
		return nil, fmt.Errorf("bad file attr size %d", file.hdr.AttrSize)
	}

	// hdr.Data.Size is the last thing written out by perf, so if
	// it's zero, we're working with a partial file.
	if file.hdr.Data.Size == 0 {
		return nil, fmt.Errorf("truncated data file")
	}

	// Read EventAttrs
	if err := readSlice(file.hdr.Attrs.sectionReader(r), &file.attrs); err != nil {
		return nil, err
	}
	// Check EventAttr sizes
	attrSize := uint32(binary.Size(&EventAttr{}))
	for _, attr := range file.attrs {
		if attr.Attr.Size != attrSize {
			return nil, fmt.Errorf("bad event attr size %d", attr.Attr.Size)
		}
	}

	// Read EventAttr IDs and create ID -> EventAttr map
	file.idToAttr = make(map[attrID]*EventAttr)
	for _, attr := range file.attrs {
		var ids []attrID
		if err := readSlice(attr.IDs.sectionReader(r), &ids); err != nil {
			return nil, err
		}<commit_msg>perffile: improve error for truncated data files<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package perffile

import (
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"reflect"
	"sort"
)

// TODO: Type for file format errors.

// A File is a perf.data file. It consists of a sequence of records,
// which can be retrieved with the Records method, as well as several
// optional metadata fields.
type File struct {
	r      io.ReaderAt
	closer io.Closer
	hdr    fileHeader

	attrs    []fileAttr
	idToAttr map[attrID]*EventAttr

	featureSections map[feature]fileSection

	sampleIDOffset int // byte offset of AttrID in sample

	sampleIDAll    bool // non-samples have sample_id trailer
	recordIDOffset int  // byte offset of AttrID in non-sample, from end
}

// New reads a "perf.data" file from r.
//
// The caller must keep r open as long as it is using the returned
// *File.
func New(r io.ReaderAt) (*File, error) {
	file := &File{r: r}

	// See perf_session__read_header in tools/perf/util/header.c

	// Read header
	sr := io.NewSectionReader(r, 0, 1024)
	if err := binary.Read(sr, binary.LittleEndian, &file.hdr); err != nil {
		return nil, err
	}
	switch string(file.hdr.Magic[:]) {
	case "PERFILE2":
		// Version 2, little endian.
		break
	case "2ELIFREP":
		// Version 2, big endian.
		//
		// TODO: Support big endian profiles.
		return nil, fmt.Errorf("big endian profiles not supported")
	case "PERFFILE":
		// Version 1 file.
		return nil, fmt.Errorf("version 1 profiles not supported")
	default:
		return nil, fmt.Errorf("bad or unsupported file magic %q", string(file.hdr.Magic[:]))
	}
	if file.hdr.Size != uint64(binary.Size(&file.hdr)) {
		return nil, fmt.Errorf("bad header size %d", file.hdr.Size)
	}
	// TODO: perf supports attrSize 64, 72, 80, and 96 for compatibility
	if file.hdr.AttrSize != uint64(binary.Size(&fileAttr{})) {
		return nil, fmt.Errorf("bad file attr size %d", file.hdr.AttrSize)
	}

	// hdr.Data.Size is the last thing written out by perf, so if
	// it's zero, we're working with a partial file.
	if file.hdr.Data.Size == 0 {
		return nil, fmt.Errorf("truncated data file; was 'perf record' properly terminated?")
	}

	// Read EventAttrs
	if err := readSlice(file.hdr.Attrs.sectionReader(r), &file.attrs); err != nil {
		return nil, err
	}
	// Check EventAttr sizes
	attrSize := uint32(binary.Size(&EventAttr{}))
	for _, attr := range file.attrs {
		if attr.Attr.Size != attrSize {
			return nil, fmt.Errorf("bad event attr size %d", attr.Attr.Size)
		}
	}

	// Read EventAttr IDs and create ID -> EventAttr map
	file.idToAttr = make(map[attrID]*EventAttr)
	for _, attr := range file.attrs {
		var ids []attrID
		if err := readSlice(attr.IDs.sectionReader(r), &ids); err != nil {
			return nil, err
		}
{'fraction_non_alphanumeric': 0.12488928255093003, 'fraction_numerical': 0.010628875110717449, 'mean_word_length': 3.3835403726708075, 'pattern_counts': {'":': 6, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5748977', 'n_tokens_mistral': 1993, 'n_tokens_neox': 1857, 'n_words': 661}