| // Copyright 2023 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 counter | |
| import ( | |
| "bytes" | |
| "fmt" | |
| "strings" | |
| "unsafe" | |
| "golang.org/x/telemetry/internal/mmap" | |
| ) | |
| type File struct { | |
| Meta map[string]string | |
| Count map[string]uint64 | |
| } | |
| func Parse(filename string, data []byte) (*File, error) { | |
| if !bytes.HasPrefix(data, []byte(hdrPrefix)) || len(data) < pageSize { | |
| if len(data) < pageSize { | |
| return nil, fmt.Errorf("%s: file too short (%d<%d)", filename, len(data), pageSize) | |
| } | |
| return nil, fmt.Errorf("%s: wrong hdr (not %q)", filename, hdrPrefix) | |
| } | |
| corrupt := func() (*File, error) { | |
| // TODO(rfindley): return a useful error message. | |
| return nil, fmt.Errorf("%s: corrupt counter file", filename) | |
| } | |
| f := &File{ | |
| Meta: make(map[string]string), | |
| Count: make(map[string]uint64), | |
| } | |
| np := round(len(hdrPrefix), 4) | |
| hdrLen := *(*uint32)(unsafe.Pointer(&data[np])) | |
| if hdrLen > pageSize { | |
| return corrupt() | |
| } | |
| meta := data[np+4 : hdrLen] | |
| if i := bytes.IndexByte(meta, 0); i >= 0 { | |
| meta = meta[:i] | |
| } | |
| m := &mappedFile{ | |
| meta: string(meta), | |
| hdrLen: hdrLen, | |
| mapping: &mmap.Data{Data: data}, | |
| } | |
| lines := strings.Split(m.meta, "\n") | |
| for _, line := range lines { | |
| if line == "" { | |
| continue | |
| } | |
| k, v, ok := strings.Cut(line, ": ") | |
| if !ok { | |
| return corrupt() | |
| } | |
| f.Meta[k] = v | |
| } | |
| for i := uint32(0); i < numHash; i++ { | |
| headOff := hdrLen + hashOff + i*4 | |
| head := m.load32(headOff) | |
| off := head | |
| for off != 0 { | |
| ename, next, v, ok := m.entryAt(off) | |
| if !ok { | |
| return corrupt() | |
| } | |
| if _, ok := f.Count[string(ename)]; ok { | |
| return corrupt() | |
| } | |
| ctrName := DecodeStack(string(ename)) | |
| f.Count[ctrName] = v.Load() | |
| off = next | |
| } | |
| } | |
| return f, nil | |
| } |