blob: a99cbade8bb04e93886b2d4aa04425c670c88f6b [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"
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -070022 "android/soong/cc/config"
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070023)
24
25var (
26 // Add flags to ignore warnings that profiles are old or missing for
27 // some functions
Pirama Arumuga Nainarf4c0baf2017-09-28 14:35:15 -070028 profileUseOtherFlags = []string{"-Wno-backend-plugin"}
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070029)
30
31const pgoProfileProject = "toolchain/pgo-profiles"
32
33const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
34const profileSamplingFlag = "-gline-tables-only"
35const profileUseInstrumentFormat = "-fprofile-use=%s"
36const profileUseSamplingFormat = "-fprofile-sample-use=%s"
37
38type PgoProperties struct {
39 Pgo struct {
40 Instrumentation *bool
41 Sampling *bool
42 Profile_file *string `android:"arch_variant"`
43 Benchmarks []string
44 } `android:"arch_variant"`
45
46 PgoPresent bool `blueprint:"mutated"`
47 ShouldProfileModule bool `blueprint:"mutated"`
48}
49
50type pgo struct {
51 Properties PgoProperties
52}
53
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -070054func (props *PgoProperties) isInstrumentation() bool {
55 return props.Pgo.Instrumentation != nil && *props.Pgo.Instrumentation == true
56}
57
58func (props *PgoProperties) isSampling() bool {
59 return props.Pgo.Sampling != nil && *props.Pgo.Sampling == true
60}
61
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070062func (pgo *pgo) props() []interface{} {
63 return []interface{}{&pgo.Properties}
64}
65
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -070066func (pgo *pgo) addProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
67 if pgo.Properties.isInstrumentation() {
68 flags.CFlags = append(flags.CFlags, profileInstrumentFlag)
69 // The profile runtime is added below in deps(). Add the below
70 // flag, which is the only other link-time action performed by
71 // the Clang driver during link.
72 flags.LdFlags = append(flags.LdFlags, "-u__llvm_profile_runtime")
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070073 }
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -070074 if pgo.Properties.isSampling() {
75 flags.CFlags = append(flags.CFlags, profileSamplingFlag)
76 flags.LdFlags = append(flags.LdFlags, profileSamplingFlag)
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070077 }
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -070078 return flags
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070079}
80
81func (pgo *pgo) profileUseFlag(ctx ModuleContext, file string) string {
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -070082 if pgo.Properties.isInstrumentation() {
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070083 return fmt.Sprintf(profileUseInstrumentFormat, file)
84 }
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -070085 if pgo.Properties.isSampling() {
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -070086 return fmt.Sprintf(profileUseSamplingFormat, file)
87 }
88 return ""
89}
90
91func (pgo *pgo) profileUseFlags(ctx ModuleContext, file string) []string {
92 flags := []string{pgo.profileUseFlag(ctx, file)}
93 flags = append(flags, profileUseOtherFlags...)
94 return flags
95}
96
97func (props *PgoProperties) isPGO(ctx BaseModuleContext) bool {
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -070098 isInstrumentation := props.isInstrumentation()
99 isSampling := props.isSampling()
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -0700100
101 profileKindPresent := isInstrumentation || isSampling
102 filePresent := props.Pgo.Profile_file != nil
103 benchmarksPresent := len(props.Pgo.Benchmarks) > 0
104
105 // If all three properties are absent, PGO is OFF for this module
106 if !profileKindPresent && !filePresent && !benchmarksPresent {
107 return false
108 }
109
110 // If at least one property exists, validate that all properties exist
111 if !profileKindPresent || !filePresent || !benchmarksPresent {
112 var missing []string
113 if !profileKindPresent {
114 missing = append(missing, "profile kind (either \"instrumentation\" or \"sampling\" property)")
115 }
116 if !filePresent {
117 missing = append(missing, "profile_file property")
118 }
119 if !benchmarksPresent {
120 missing = append(missing, "non-empty benchmarks property")
121 }
122 missingProps := strings.Join(missing, ", ")
123 ctx.ModuleErrorf("PGO specification is missing properties: " + missingProps)
124 }
125
126 // Sampling not supported yet
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -0700127 if isSampling {
128 ctx.PropertyErrorf("pgo.sampling", "\"sampling\" is not supported yet)")
129 }
130
Pirama Arumuga Nainar6fc8d912017-10-05 10:25:00 -0700131 if isSampling && isInstrumentation {
132 ctx.PropertyErrorf("pgo", "Exactly one of \"instrumentation\" and \"sampling\" properties must be set")
133 }
134
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -0700135 return true
136}
137
138func getPgoProfilesDir(ctx ModuleContext) android.OptionalPath {
139 return android.ExistentPathForSource(ctx, "", pgoProfileProject)
140}
141
142func (pgo *pgo) begin(ctx BaseModuleContext) {
143 // TODO Evaluate if we need to support PGO for host modules
144 if ctx.Host() {
145 return
146 }
147
148 // Check if PGO is needed for this module
149 pgo.Properties.PgoPresent = pgo.Properties.isPGO(ctx)
150
151 if !pgo.Properties.PgoPresent {
152 return
153 }
154
155 // This module should be instrumented if ANDROID_PGO_INSTRUMENT is set
156 // and includes a benchmark listed for this module
157 //
158 // TODO Validate that each benchmark instruments at least one module
159 pgo.Properties.ShouldProfileModule = false
160 pgoBenchmarks := ctx.AConfig().Getenv("ANDROID_PGO_INSTRUMENT")
161 pgoBenchmarksMap := make(map[string]bool)
162 for _, b := range strings.Split(pgoBenchmarks, ",") {
163 pgoBenchmarksMap[b] = true
164 }
165
166 for _, b := range pgo.Properties.Pgo.Benchmarks {
167 if pgoBenchmarksMap[b] == true {
168 pgo.Properties.ShouldProfileModule = true
169 break
170 }
171 }
172}
173
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -0700174func (pgo *pgo) deps(ctx BaseModuleContext, deps Deps) Deps {
175 if pgo.Properties.ShouldProfileModule {
176 runtimeLibrary := config.ProfileRuntimeLibrary(ctx.toolchain())
177 deps.LateStaticLibs = append(deps.LateStaticLibs, runtimeLibrary)
178 }
179 return deps
180}
181
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -0700182func (pgo *pgo) flags(ctx ModuleContext, flags Flags) Flags {
183 if ctx.Host() {
184 return flags
185 }
186
187 props := pgo.Properties
188
189 // Add flags to profile this module based on its profile_kind
190 if props.ShouldProfileModule {
Pirama Arumuga Nainar49b53d52017-10-04 16:47:29 -0700191 return pgo.addProfileGatherFlags(ctx, flags)
Pirama Arumuga Nainarada83ec2017-08-31 23:38:27 -0700192 }
193
194 // If the PGO profiles project is found, and this module has PGO
195 // enabled, add flags to use the profile
196 if profilesDir := getPgoProfilesDir(ctx); props.PgoPresent && profilesDir.Valid() {
197 profileFile := android.PathForSource(ctx, profilesDir.String(), *(props.Pgo.Profile_file))
198 profileUseFlags := pgo.profileUseFlags(ctx, profileFile.String())
199
200 flags.CFlags = append(flags.CFlags, profileUseFlags...)
201 flags.LdFlags = append(flags.LdFlags, profileUseFlags...)
202
203 // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
204 // if profileFile gets updated
205 flags.CFlagsDeps = append(flags.CFlagsDeps, profileFile)
206 flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFile)
207 }
208
209 return flags
210}