| // 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] } |