1// Copyright 2022 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 "strings" 19 "testing" 20 21 "android/soong/android" 22 23 "github.com/google/blueprint" 24) 25 26type visitDirectDepsInterface interface { 27 VisitDirectDeps(blueprint.Module, func(dep blueprint.Module)) 28} 29 30func hasDirectDep(ctx visitDirectDepsInterface, m android.Module, wantDep android.Module) bool { 31 var found bool 32 ctx.VisitDirectDeps(m, func(dep blueprint.Module) { 33 if dep == wantDep { 34 found = true 35 } 36 }) 37 return found 38} 39 40func TestAfdoDeps(t *testing.T) { 41 t.Parallel() 42 bp := ` 43 cc_library_shared { 44 name: "libTest", 45 srcs: ["test.c"], 46 static_libs: ["libFoo"], 47 afdo: true, 48 } 49 50 cc_library_static { 51 name: "libFoo", 52 srcs: ["foo.c"], 53 static_libs: ["libBar"], 54 } 55 56 cc_library_static { 57 name: "libBar", 58 srcs: ["bar.c"], 59 } 60 ` 61 62 result := android.GroupFixturePreparers( 63 PrepareForTestWithFdoProfile, 64 prepareForCcTest, 65 android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), 66 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 67 variables.AfdoProfiles = []string{ 68 "libTest://afdo_profiles_package:libTest_afdo", 69 } 70 }), 71 android.MockFS{ 72 "afdo_profiles_package/Android.bp": []byte(` 73 fdo_profile { 74 name: "libTest_afdo", 75 profile: "libTest.afdo", 76 } 77 `), 78 }.AddToFixture(), 79 ).RunTestWithBp(t, bp) 80 81 expectedCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo" 82 83 libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") 84 libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") 85 libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest") 86 87 // Check cFlags of afdo-enabled module and the afdo-variant of its static deps 88 cFlags := libTest.Rule("cc").Args["cFlags"] 89 if !strings.Contains(cFlags, expectedCFlag) { 90 t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) 91 } 92 93 cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"] 94 if !strings.Contains(cFlags, expectedCFlag) { 95 t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) 96 } 97 98 cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"] 99 if !strings.Contains(cFlags, expectedCFlag) { 100 t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) 101 } 102 103 // Check dependency edge from afdo-enabled module to static deps 104 if !hasDirectDep(result, libTest.Module(), libFooAfdoVariant.Module()) { 105 t.Errorf("libTest missing dependency on afdo variant of libFoo") 106 } 107 108 if !hasDirectDep(result, libFooAfdoVariant.Module(), libBarAfdoVariant.Module()) { 109 t.Errorf("libTest missing dependency on afdo variant of libBar") 110 } 111 112 // Verify non-afdo variant exists and doesn't contain afdo 113 libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static") 114 libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static") 115 116 cFlags = libFoo.Rule("cc").Args["cFlags"] 117 if strings.Contains(cFlags, expectedCFlag) { 118 t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags) 119 } 120 cFlags = libBar.Rule("cc").Args["cFlags"] 121 if strings.Contains(cFlags, expectedCFlag) { 122 t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags) 123 } 124 125 // Check dependency edges of static deps 126 if hasDirectDep(result, libTest.Module(), libFoo.Module()) { 127 t.Errorf("libTest should not depend on non-afdo variant of libFoo") 128 } 129 130 if !hasDirectDep(result, libFoo.Module(), libBar.Module()) { 131 t.Errorf("libFoo missing dependency on non-afdo variant of libBar") 132 } 133} 134 135func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) { 136 t.Parallel() 137 bp := ` 138 cc_library_shared { 139 name: "libTest", 140 srcs: ["foo.c"], 141 static_libs: ["libFoo"], 142 } 143 144 cc_library_static { 145 name: "libFoo", 146 srcs: ["foo.c"], 147 static_libs: ["libBar"], 148 afdo: true, // TODO(b/256670524): remove support for enabling afdo from static only libraries, this can only propagate from shared libraries/binaries 149 } 150 151 cc_library_static { 152 name: "libBar", 153 } 154 ` 155 156 result := android.GroupFixturePreparers( 157 prepareForCcTest, 158 PrepareForTestWithFdoProfile, 159 android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libFoo.afdo", ""), 160 android.MockFS{ 161 "afdo_profiles_package/Android.bp": []byte(` 162 soong_namespace { 163 } 164 fdo_profile { 165 name: "libFoo_afdo", 166 profile: "libFoo.afdo", 167 } 168 `), 169 }.AddToFixture(), 170 ).RunTestWithBp(t, bp) 171 172 libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module() 173 libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static") 174 libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static").Module() 175 176 if !hasDirectDep(result, libTest, libFoo.Module()) { 177 t.Errorf("libTest missing dependency on afdo variant of libFoo") 178 } 179 180 if !hasDirectDep(result, libFoo.Module(), libBar) { 181 t.Errorf("libFoo missing dependency on afdo variant of libBar") 182 } 183 184 fooVariants := result.ModuleVariantsForTests("foo") 185 for _, v := range fooVariants { 186 if strings.Contains(v, "afdo-") { 187 t.Errorf("Expected no afdo variant of 'foo', got %q", v) 188 } 189 } 190 191 cFlags := libFoo.Rule("cc").Args["cFlags"] 192 if w := "-fprofile-sample-accurate"; strings.Contains(cFlags, w) { 193 t.Errorf("Expected 'foo' to not enable afdo, but found %q in cflags %q", w, cFlags) 194 } 195 196 barVariants := result.ModuleVariantsForTests("bar") 197 for _, v := range barVariants { 198 if strings.Contains(v, "afdo-") { 199 t.Errorf("Expected no afdo variant of 'bar', got %q", v) 200 } 201 } 202} 203 204func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) { 205 bp := ` 206 cc_library { 207 name: "libTest", 208 srcs: ["foo.c"], 209 runtime_libs: ["libFoo"], 210 afdo: true, 211 } 212 213 cc_library { 214 name: "libFoo", 215 } 216 ` 217 218 result := android.GroupFixturePreparers( 219 prepareForCcTest, 220 PrepareForTestWithFdoProfile, 221 android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), 222 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 223 variables.AfdoProfiles = []string{ 224 "libTest://afdo_profiles_package:libTest_afdo", 225 } 226 }), 227 android.MockFS{ 228 "afdo_profiles_package/Android.bp": []byte(` 229 fdo_profile { 230 name: "libTest_afdo", 231 profile: "libTest.afdo", 232 } 233 `), 234 }.AddToFixture(), 235 ).RunTestWithBp(t, bp) 236 237 libFooVariants := result.ModuleVariantsForTests("libFoo") 238 for _, v := range libFooVariants { 239 if strings.Contains(v, "afdo-") { 240 t.Errorf("Expected no afdo variant of 'foo', got %q", v) 241 } 242 } 243} 244 245func TestAfdoEnabledWithMultiArchs(t *testing.T) { 246 bp := ` 247 cc_library_shared { 248 name: "foo", 249 srcs: ["test.c"], 250 afdo: true, 251 compile_multilib: "both", 252 } 253` 254 result := android.GroupFixturePreparers( 255 PrepareForTestWithFdoProfile, 256 prepareForCcTest, 257 android.FixtureAddTextFile("afdo_profiles_package/foo_arm.afdo", ""), 258 android.FixtureAddTextFile("afdo_profiles_package/foo_arm64.afdo", ""), 259 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 260 variables.AfdoProfiles = []string{ 261 "foo://afdo_profiles_package:foo_afdo", 262 } 263 }), 264 android.MockFS{ 265 "afdo_profiles_package/Android.bp": []byte(` 266 soong_namespace { 267 } 268 fdo_profile { 269 name: "foo_afdo", 270 arch: { 271 arm: { 272 profile: "foo_arm.afdo", 273 }, 274 arm64: { 275 profile: "foo_arm64.afdo", 276 } 277 } 278 } 279 `), 280 }.AddToFixture(), 281 ).RunTestWithBp(t, bp) 282 283 fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared") 284 fooArmCFlags := fooArm.Rule("cc").Args["cFlags"] 285 if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) { 286 t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArmCFlags) 287 } 288 289 fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a_shared") 290 fooArm64CFlags := fooArm64.Rule("cc").Args["cFlags"] 291 if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) { 292 t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArm64CFlags) 293 } 294} 295 296func TestMultipleAfdoRDeps(t *testing.T) { 297 t.Parallel() 298 bp := ` 299 cc_library_shared { 300 name: "libTest", 301 srcs: ["test.c"], 302 static_libs: ["libFoo"], 303 afdo: true, 304 } 305 306 cc_library_shared { 307 name: "libBar", 308 srcs: ["bar.c"], 309 static_libs: ["libFoo"], 310 afdo: true, 311 } 312 313 cc_library_static { 314 name: "libFoo", 315 srcs: ["foo.c"], 316 } 317 ` 318 319 result := android.GroupFixturePreparers( 320 PrepareForTestWithFdoProfile, 321 prepareForCcTest, 322 android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), 323 android.FixtureAddTextFile("afdo_profiles_package/libBar.afdo", ""), 324 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 325 variables.AfdoProfiles = []string{ 326 "libTest://afdo_profiles_package:libTest_afdo", 327 "libBar://afdo_profiles_package:libBar_afdo", 328 } 329 }), 330 android.MockFS{ 331 "afdo_profiles_package/Android.bp": []byte(` 332 fdo_profile { 333 name: "libTest_afdo", 334 profile: "libTest.afdo", 335 } 336 fdo_profile { 337 name: "libBar_afdo", 338 profile: "libBar.afdo", 339 } 340 `), 341 }.AddToFixture(), 342 ).RunTestWithBp(t, bp) 343 344 expectedCFlagLibTest := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo" 345 expectedCFlagLibBar := "-fprofile-sample-use=afdo_profiles_package/libBar.afdo" 346 347 libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") 348 libFooAfdoVariantWithLibTest := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") 349 350 libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_shared") 351 libFooAfdoVariantWithLibBar := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar") 352 353 // Check cFlags of afdo-enabled module and the afdo-variant of its static deps 354 cFlags := libTest.Rule("cc").Args["cFlags"] 355 if !strings.Contains(cFlags, expectedCFlagLibTest) { 356 t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) 357 } 358 cFlags = libBar.Rule("cc").Args["cFlags"] 359 if !strings.Contains(cFlags, expectedCFlagLibBar) { 360 t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) 361 } 362 363 cFlags = libFooAfdoVariantWithLibTest.Rule("cc").Args["cFlags"] 364 if !strings.Contains(cFlags, expectedCFlagLibTest) { 365 t.Errorf("Expected 'libFooAfdoVariantWithLibTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) 366 } 367 368 cFlags = libFooAfdoVariantWithLibBar.Rule("cc").Args["cFlags"] 369 if !strings.Contains(cFlags, expectedCFlagLibBar) { 370 t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) 371 } 372 373 // Check dependency edges of static deps 374 if !hasDirectDep(result, libTest.Module(), libFooAfdoVariantWithLibTest.Module()) { 375 t.Errorf("libTest missing dependency on afdo variant of libFoo") 376 } 377 378 if !hasDirectDep(result, libBar.Module(), libFooAfdoVariantWithLibBar.Module()) { 379 t.Errorf("libFoo missing dependency on non-afdo variant of libBar") 380 } 381} 382 383func TestAfdoDepsWithoutProfile(t *testing.T) { 384 t.Parallel() 385 bp := ` 386 cc_library_shared { 387 name: "libTest", 388 srcs: ["test.c"], 389 static_libs: ["libFoo"], 390 afdo: true, 391 } 392 393 cc_library_static { 394 name: "libFoo", 395 srcs: ["foo.c"], 396 static_libs: ["libBar"], 397 } 398 399 cc_library_static { 400 name: "libBar", 401 srcs: ["bar.c"], 402 } 403 ` 404 405 result := android.GroupFixturePreparers( 406 PrepareForTestWithFdoProfile, 407 prepareForCcTest, 408 ).RunTestWithBp(t, bp) 409 410 // Even without a profile path, the afdo enabled libraries should be built with 411 // -funique-internal-linkage-names. 412 expectedCFlag := "-funique-internal-linkage-names" 413 414 libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") 415 libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") 416 libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest") 417 418 // Check cFlags of afdo-enabled module and the afdo-variant of its static deps 419 cFlags := libTest.Rule("cc").Args["cFlags"] 420 if !strings.Contains(cFlags, expectedCFlag) { 421 t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) 422 } 423 424 cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"] 425 if !strings.Contains(cFlags, expectedCFlag) { 426 t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) 427 } 428 429 cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"] 430 if !strings.Contains(cFlags, expectedCFlag) { 431 t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) 432 } 433 // Check dependency edge from afdo-enabled module to static deps 434 if !hasDirectDep(result, libTest.Module(), libFooAfdoVariant.Module()) { 435 t.Errorf("libTest missing dependency on afdo variant of libFoo") 436 } 437 438 if !hasDirectDep(result, libFooAfdoVariant.Module(), libBarAfdoVariant.Module()) { 439 t.Errorf("libTest missing dependency on afdo variant of libBar") 440 } 441 442 // Verify non-afdo variant exists and doesn't contain afdo 443 libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static") 444 libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static") 445 446 cFlags = libFoo.Rule("cc").Args["cFlags"] 447 if strings.Contains(cFlags, expectedCFlag) { 448 t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags) 449 } 450 cFlags = libBar.Rule("cc").Args["cFlags"] 451 if strings.Contains(cFlags, expectedCFlag) { 452 t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags) 453 } 454 455 // Check dependency edges of static deps 456 if hasDirectDep(result, libTest.Module(), libFoo.Module()) { 457 t.Errorf("libTest should not depend on non-afdo variant of libFoo") 458 } 459 460 if !hasDirectDep(result, libFoo.Module(), libBar.Module()) { 461 t.Errorf("libFoo missing dependency on non-afdo variant of libBar") 462 } 463} 464