blob: 0bd85e729299d686329e53e1cb6ef0f227f0340d [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"sort"
"android.googlesource.com/platform/tools/gpu/framework/id"
"android.googlesource.com/platform/tools/gpu/framework/log"
"android.googlesource.com/platform/tools/gpu/framework/stringtable/msg"
)
// ReportBuilder helps construct report in a cleaner way.
type ReportBuilder struct {
report *Report // Report to build.
tagIDs map[string]uint32 // Map of Msg.Identifier to index in Report for tags.
tagArgs map[id.ID]uint32 // Map of msg.Arg for arguments to index in Report of tags.
tagRefs map[id.ID]MsgRef // Key: hash of TagRef, Value: TagRef used for hashing.
tagIDCount uint32 // Count of unique tag identifiers.
tagArgCount uint32 // Count of unique tag arguments.
}
// ReportItemRaw represents ReportItem, raw message and array of raw tags.
type ReportItemRaw struct {
Item ReportItem
Message *msg.Msg
Tags []*msg.Msg
}
// NewReportBuilder creates and initializes new report builder.
func NewReportBuilder() *ReportBuilder {
builder := &ReportBuilder{}
builder.report = &Report{}
builder.tagIDs = map[string]uint32{}
builder.tagArgs = map[id.ID]uint32{}
builder.tagRefs = map[id.ID]MsgRef{}
return builder
}
// Add processes tags, adds references to item and adds item to report.
func (builder *ReportBuilder) Add(ctx log.Context, element *ReportItemRaw) {
if err := builder.processMessages(&element.Item, element.Message, element.Tags); err == nil {
builder.report.Items = append(builder.report.Items, element.Item)
} else {
ctx.Error().Logf("Error %v during adding an item to a report", err)
}
}
// SortReport sorts report.
func (builder *ReportBuilder) SortReport() {
sort.Stable(reportSorter(builder.report.Items))
}
// Build performs final processing and returns report.
func (builder *ReportBuilder) Build() *Report {
if err := builder.processGroups(); err != nil {
return nil
}
return builder.report
}
// WrapReportItem wraps ReportItem into raw representation of ReportItemTagged
// which contains raw messages instead of references.
func WrapReportItem(item ReportItem, m *msg.Msg) *ReportItemRaw {
return &ReportItemRaw{
Item: item,
Message: m,
}
}
// processMessages checks if message's and tags' identifier and arguments
// are unique and treat them appropriately. It also adds message references
// to report item.
func (builder *ReportBuilder) processMessages(item *ReportItem, message *msg.Msg, tags []*msg.Msg) error {
ref, err := builder.processMessage(message)
if err != nil {
return err
}
item.Message = ref
for _, m := range tags {
ref, err = builder.processMessage(m)
if err != nil {
return err
}
item.Tags = append(item.Tags, *ref)
}
return nil
}
func (builder *ReportBuilder) processMessage(message *msg.Msg) (*MsgRef, error) {
var ref MsgRef
// Checks if we've met a tag with the same ID.
if _, ok := builder.tagIDs[message.Identifier]; !ok {
builder.tagIDs[message.Identifier] = builder.tagIDCount
builder.report.MsgIdentifiers = append(builder.report.MsgIdentifiers,
message.Identifier)
builder.tagIDCount++
}
ref.Identifier = builder.tagIDs[message.Identifier]
for k := range message.Arguments {
arg := msg.Arg{Key: k, Value: message.Arguments[k]}
h, err := arg.Hash()
if err != nil {
return nil, err
}
// Checks if we've met a tag with the same hash of arguments.
if _, ok := builder.tagArgs[h]; !ok {
builder.tagArgs[h] = builder.tagArgCount
builder.report.MsgArguments = append(builder.report.MsgArguments, arg)
builder.tagArgCount++
}
ref.Arguments = append(ref.Arguments, builder.tagArgs[h])
}
return &ref, nil
}
// processGroups forms group iterating over ReportItem and comparing their messages.
func (builder *ReportBuilder) processGroups() error {
groupItems := map[id.ID][]uint32{}
tags := map[id.ID]MsgRef{}
for i, item := range builder.report.Items {
ref := *item.Message
sort.Stable(uint32Slice(ref.Arguments))
refHash, err := ref.Hash()
if err != nil {
return err
}
if _, ok := groupItems[refHash]; ok {
groupItems[refHash] = append(groupItems[refHash], uint32(i))
} else {
groupItems[refHash] = []uint32{uint32(i)}
tags[refHash] = ref
}
}
for refHash, items := range groupItems {
builder.report.Groups = append(builder.report.Groups, ReportGroup{
Name: tags[refHash],
Items: items,
})
}
return nil
}
// Used for sorting report items
type reportSorter []ReportItem
func (s reportSorter) Len() int { return len(s) }
func (s reportSorter) Less(i, j int) bool { return s[i].Atom < s[j].Atom }
func (s reportSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// uint32Slice attaches the methods of sort.Interface to []uint32, sorting in increasing order.
type uint32Slice []uint32
func (s uint32Slice) Len() int { return len(s) }
func (s uint32Slice) Less(i, j int) bool { return s[i] < s[j] }
func (s uint32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }