blob: f9c8bbf9335765a4c7923f0df0555947e6b18aa1 [file] [log] [blame]
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -07001// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cc
16
17import (
18 "fmt"
19 "strings"
20
21 "android/soong/android"
22)
23
24var (
25 // Add flags to ignore warnings that profiles are old or missing for
26 // some functions
Pirama Arumuga Nainarf4c0baf2017-09-28 14:35:15 -070027 profileUseOtherFlags = []string{"-Wno-backend-plugin"}
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070028)
29
30const pgoProfileProject = "toolchain/pgo-profiles"
31
32const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
33const profileSamplingFlag = "-gline-tables-only"
34const profileUseInstrumentFormat = "-fprofile-use=%s"
35const profileUseSamplingFormat = "-fprofile-sample-use=%s"
36
37type PgoProperties struct {
38 Pgo struct {
39 Instrumentation *bool
40 Sampling *bool
41 Profile_file *string `android:"arch_variant"`
42 Benchmarks []string
43 } `android:"arch_variant"`
44
45 PgoPresent bool `blueprint:"mutated"`
46 ShouldProfileModule bool `blueprint:"mutated"`
47}
48
49type pgo struct {
50 Properties PgoProperties
51}
52
53func (pgo *pgo) props() []interface{} {
54 return []interface{}{&pgo.Properties}
55}
56
57func (pgo *pgo) profileGatherFlags(ctx ModuleContext) string {
58 if *pgo.Properties.Pgo.Instrumentation {
59 return profileInstrumentFlag
60 }
61 if *pgo.Properties.Pgo.Sampling {
62 return profileSamplingFlag
63 }
64 return ""
65}
66
67func (pgo *pgo) profileUseFlag(ctx ModuleContext, file string) string {
68 if *pgo.Properties.Pgo.Instrumentation {
69 return fmt.Sprintf(profileUseInstrumentFormat, file)
70 }
71 if *pgo.Properties.Pgo.Sampling {
72 return fmt.Sprintf(profileUseSamplingFormat, file)
73 }
74 return ""
75}
76
77func (pgo *pgo) profileUseFlags(ctx ModuleContext, file string) []string {
78 flags := []string{pgo.profileUseFlag(ctx, file)}
79 flags = append(flags, profileUseOtherFlags...)
80 return flags
81}
82
83func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool {
84 isInstrumentation := props.Pgo.Instrumentation != nil
85 isSampling := props.Pgo.Sampling != nil
86
87 profileKindPresent := isInstrumentation || isSampling
88 filePresent := props.Pgo.Profile_file != nil
89 benchmarksPresent := len(props.Pgo.Benchmarks) > 0
90
91 // If all three properties are absent, PGO is OFF for this module
92 if !profileKindPresent && !filePresent && !benchmarksPresent {
93 return false
94 }
95
96 // If at least one property exists, validate that all properties exist
97 if !profileKindPresent || !filePresent || !benchmarksPresent {
98 var missing []string
99 if !profileKindPresent {
100 missing = append(missing, "profile kind (either \"instrumentation\" or \"sampling\" property)")
101 }
102 if !filePresent {
103 missing = append(missing, "profile_file property")
104 }
105 if !benchmarksPresent {
106 missing = append(missing, "non-empty benchmarks property")
107 }
108 missingProps := strings.Join(missing, ", ")
109 ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps)
110 }
111
112 // Sampling not supported yet
113 //
114 // TODO When sampling support is turned on, check that instrumentation and
115 // sampling are not simultaneously specified
116 if isSampling {
117 ctx.PropertyErrorf("pgo.sampling", "\"sampling\" is not supported yet)")
118 }
119
120 return true
121}
122
123func getPgoProfilesDir(ctx ModuleContext) android.OptionalPath {
124 return android.ExistentPathForSource(ctx, "", pgoProfileProject)
125}
126
127func (pgo *pgo) begin(ctx BaseModuleContext) {
128 // TODO Evaluate if we need to support PGO for host modules
129 if ctx.Host() {
130 return
131 }
132
133 // Check if PGO is needed for this module
134 pgo.Properties.PgoPresent = pgo.Properties.isPGO(ctx)
135
136 if !pgo.Properties.PgoPresent {
137 return
138 }
139
140 // This module should be instrumented if ANDROID_PGO_INSTRUMENT is set
141 // and includes a benchmark listed for this module
142 //
143 // TODO Validate that each benchmark instruments at least one module
144 pgo.Properties.ShouldProfileModule = false
145 pgoBenchmarks := ctx.AConfig().Getenv("ANDROID_PGO_INSTRUMENT")
146 pgoBenchmarksMap := make(map[string]bool)
147 for _, b := range strings.Split(pgoBenchmarks, ",") {
148 pgoBenchmarksMap[b] = true
149 }
150
151 for _, b := range pgo.Properties.Pgo.Benchmarks {
152 if pgoBenchmarksMap[b] == true {
153 pgo.Properties.ShouldProfileModule = true
154 break
155 }
156 }
157}
158
159func (pgo *pgo) flags(ctx ModuleContext, flags Flags) Flags {
160 if ctx.Host() {
161 return flags
162 }
163
164 props := pgo.Properties
165
166 // Add flags to profile this module based on its profile_kind
167 if props.ShouldProfileModule {
168 profileGatherFlags := pgo.profileGatherFlags(ctx)
169 flags.LdFlags = append(flags.LdFlags, profileGatherFlags)
170 flags.CFlags = append(flags.CFlags, profileGatherFlags)
171 return flags
172 }
173
174 // If the PGO profiles project is found, and this module has PGO
175 // enabled, add flags to use the profile
176 if profilesDir := getPgoProfilesDir(ctx); props.PgoPresent && profilesDir.Valid() {
177 profileFile := android.PathForSource(ctx, profilesDir.String(), *(props.Pgo.Profile_file))
178 profileUseFlags := pgo.profileUseFlags(ctx, profileFile.String())
179
180 flags.CFlags = append(flags.CFlags, profileUseFlags...)
181 flags.LdFlags = append(flags.LdFlags, profileUseFlags...)
182
183 // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
184 // if profileFile gets updated
185 flags.CFlagsDeps = append(flags.CFlagsDeps, profileFile)
186 flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFile)
187 }
188
189 return flags
190}