Skip to content

Commit e13c3c7

Browse files
iancha1992kkressfmeum
authored
[6.3.0] Let common ignore unsupported options (with added commit 3dc6951) (#18609)
* Change --memory_profile_stable_heap_parameters to accept more than one GC specification Currently memory_profile_stable_heap_parameters expects 2 ints and runs that many GCs with pauses between them 2nd param. This CL doesn't change that, but allows any arbitrary number of pairs to be provided that will run the same logic for each pair. This allows experimenting with forcing longer pauses on that thread before doing the quick GCs that allow for cleaner memory measurement. PiperOrigin-RevId: 485646588 Change-Id: Iff4f17cdaae409854f99397b4271bb5f87c4c404 * Let `common` ignore unsupported options Fixes #3054 RELNOTES: Options specified on the pseudo-command `common` in `.rc` files are now ignored by commands that do not support them as long as they are valid options for *any* Bazel command. Previously, commands that did not support all options given for `common` would fail to run. These previous semantics of `common` are now available via the new `always` pseudo-command. Closes #18130. PiperOrigin-RevId: 534940403 Change-Id: I2ae268ae84de3f2b07ff3d1e36bf382bce714968 --------- Co-authored-by: Googler <[email protected]> Co-authored-by: Fabian Meumertzheim <[email protected]>
1 parent 092ef1c commit e13c3c7

20 files changed

+936
-233
lines changed

site/en/run/bazelrc.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,14 @@ line specifies when these defaults are applied:
103103
104104
- `startup`: startup options, which go before the command, and are described
105105
in `bazel help startup_options`.
106-
- `common`: options that apply to all Bazel commands.
106+
- `common`: options that should be applied to all Bazel commands that support
107+
them. If a command does not support an option specified in this way, the
108+
option is ignored so long as it is valid for *some* other Bazel command.
109+
Note that this only applies to option names: If the current command accepts
110+
an option with the specified name, but doesn't support the specified value,
111+
it will fail.
112+
- `always`: options that apply to all Bazel commands. If a command does not
113+
support an option specified in this way, it will fail.
107114
- _`command`_: Bazel command, such as `build` or `query` to which the options
108115
apply. These options also apply to all commands that inherit from the
109116
specified command. (For example, `test` inherits from `build`.)

src/main/java/com/google/devtools/build/lib/profiler/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ java_library(
3434
"//src/main/java/com/google/devtools/build/lib/concurrent",
3535
"//src/main/java/com/google/devtools/build/lib/unix:procmeminfo_parser",
3636
"//src/main/java/com/google/devtools/build/lib/util:os",
37+
"//src/main/java/com/google/devtools/build/lib/util:pair",
3738
"//src/main/java/com/google/devtools/build/lib/worker:worker_metric",
3839
"//src/main/java/com/google/devtools/common/options",
3940
"//third_party:auto_value",

src/main/java/com/google/devtools/build/lib/profiler/MemoryProfiler.java

+56-33
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@
1818
import com.google.common.annotations.VisibleForTesting;
1919
import com.google.common.base.MoreObjects;
2020
import com.google.common.base.Splitter;
21+
import com.google.devtools.build.lib.util.Pair;
2122
import com.google.devtools.common.options.OptionsParsingException;
2223
import java.io.OutputStream;
2324
import java.io.PrintStream;
2425
import java.lang.management.ManagementFactory;
2526
import java.lang.management.MemoryMXBean;
2627
import java.lang.management.MemoryUsage;
2728
import java.time.Duration;
28-
import java.util.Iterator;
29+
import java.util.ArrayList;
30+
import java.util.List;
2931
import java.util.NoSuchElementException;
3032
import javax.annotation.Nullable;
3133

@@ -111,14 +113,29 @@ synchronized HeapAndNonHeap prepareBeanAndGetLocalMinUsage(
111113
bean.gc();
112114
MemoryUsage minHeapUsed = bean.getHeapMemoryUsage();
113115
MemoryUsage minNonHeapUsed = bean.getNonHeapMemoryUsage();
116+
114117
if (nextPhase == ProfilePhase.FINISH && memoryProfileStableHeapParameters != null) {
115-
for (int i = 1; i < memoryProfileStableHeapParameters.numTimesToDoGc; i++) {
116-
sleeper.sleep(memoryProfileStableHeapParameters.timeToSleepBetweenGcs);
117-
bean.gc();
118-
MemoryUsage currentHeapUsed = bean.getHeapMemoryUsage();
119-
if (currentHeapUsed.getUsed() < minHeapUsed.getUsed()) {
120-
minHeapUsed = currentHeapUsed;
121-
minNonHeapUsed = bean.getNonHeapMemoryUsage();
118+
for (int j = 0; j < memoryProfileStableHeapParameters.gcSpecs.size(); j++) {
119+
Pair<Integer, Duration> spec = memoryProfileStableHeapParameters.gcSpecs.get(j);
120+
121+
int numTimesToDoGc = spec.first;
122+
Duration timeToSleepBetweenGcs = spec.second;
123+
124+
for (int i = 0; i < numTimesToDoGc; i++) {
125+
// We want to skip the first cycle for the first spec, since we ran a
126+
// GC at the top of this function, but all the rest should get their
127+
// proper runs
128+
if (j == 0 && i == 0) {
129+
continue;
130+
}
131+
132+
sleeper.sleep(timeToSleepBetweenGcs);
133+
bean.gc();
134+
MemoryUsage currentHeapUsed = bean.getHeapMemoryUsage();
135+
if (currentHeapUsed.getUsed() < minHeapUsed.getUsed()) {
136+
minHeapUsed = currentHeapUsed;
137+
minNonHeapUsed = bean.getNonHeapMemoryUsage();
138+
}
122139
}
123140
}
124141
}
@@ -130,12 +147,10 @@ synchronized HeapAndNonHeap prepareBeanAndGetLocalMinUsage(
130147
* build.
131148
*/
132149
public static class MemoryProfileStableHeapParameters {
133-
private final int numTimesToDoGc;
134-
private final Duration timeToSleepBetweenGcs;
150+
private final ArrayList<Pair<Integer, Duration>> gcSpecs;
135151

136-
private MemoryProfileStableHeapParameters(int numTimesToDoGc, Duration timeToSleepBetweenGcs) {
137-
this.numTimesToDoGc = numTimesToDoGc;
138-
this.timeToSleepBetweenGcs = timeToSleepBetweenGcs;
152+
private MemoryProfileStableHeapParameters(ArrayList<Pair<Integer, Duration>> gcSpecs) {
153+
this.gcSpecs = gcSpecs;
139154
}
140155

141156
/** Converter for {@code MemoryProfileStableHeapParameters} option. */
@@ -147,40 +162,48 @@ public static class Converter
147162
@Override
148163
public MemoryProfileStableHeapParameters convert(String input)
149164
throws OptionsParsingException {
150-
Iterator<String> values = SPLITTER.split(input).iterator();
165+
List<String> values = SPLITTER.splitToList(input);
166+
167+
if (values.size() % 2 != 0) {
168+
throw new OptionsParsingException(
169+
"Expected even number of comma-separated integer values");
170+
}
171+
172+
ArrayList<Pair<Integer, Duration>> gcSpecs = new ArrayList<>(values.size() / 2);
173+
151174
try {
152-
int numTimesToDoGc = Integer.parseInt(values.next());
153-
int numSecondsToSleepBetweenGcs = Integer.parseInt(values.next());
154-
if (values.hasNext()) {
155-
throw new OptionsParsingException("Expected exactly 2 comma-separated integer values");
175+
for (int i = 0; i < values.size(); i += 2) {
176+
int numTimesToDoGc = Integer.parseInt(values.get(i));
177+
int numSecondsToSleepBetweenGcs = Integer.parseInt(values.get(i + 1));
178+
179+
if (numTimesToDoGc <= 0) {
180+
throw new OptionsParsingException("Number of times to GC must be positive");
181+
}
182+
if (numSecondsToSleepBetweenGcs < 0) {
183+
throw new OptionsParsingException(
184+
"Number of seconds to sleep between GC's must be non-negative");
185+
}
186+
gcSpecs.add(Pair.of(numTimesToDoGc, Duration.ofSeconds(numSecondsToSleepBetweenGcs)));
156187
}
157-
if (numTimesToDoGc <= 0) {
158-
throw new OptionsParsingException("Number of times to GC must be positive");
159-
}
160-
if (numSecondsToSleepBetweenGcs < 0) {
161-
throw new OptionsParsingException(
162-
"Number of seconds to sleep between GC's must be non-negative");
163-
}
164-
return new MemoryProfileStableHeapParameters(
165-
numTimesToDoGc, Duration.ofSeconds(numSecondsToSleepBetweenGcs));
188+
189+
return new MemoryProfileStableHeapParameters(gcSpecs);
166190
} catch (NumberFormatException | NoSuchElementException nfe) {
167191
throw new OptionsParsingException(
168-
"Expected exactly 2 comma-separated integer values", nfe);
192+
"Expected even number of comma-separated integer values, could not parse integer in"
193+
+ " list",
194+
nfe);
169195
}
170196
}
171197

172198
@Override
173199
public String getTypeDescription() {
174-
return "two integers, separated by a comma";
200+
return "integers, separated by a comma expected in pairs";
175201
}
176202
}
177203

178204
@Override
179205
public String toString() {
180-
return MoreObjects.toStringHelper(this)
181-
.add("numTimesToDoGc", numTimesToDoGc)
182-
.add("timeToSleepBetweenGcs", timeToSleepBetweenGcs)
183-
.toString();
206+
return MoreObjects.toStringHelper(this).add("gcSpecs", gcSpecs).toString();
184207
}
185208
}
186209

src/main/java/com/google/devtools/build/lib/runtime/BlazeOptionHandler.java

+66-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
// limitations under the License.
1414
package com.google.devtools.build.lib.runtime;
1515

16+
import static com.google.common.collect.ImmutableList.toImmutableList;
17+
1618
import com.google.common.annotations.VisibleForTesting;
1719
import com.google.common.base.Joiner;
1820
import com.google.common.collect.ArrayListMultimap;
@@ -35,6 +37,7 @@
3537
import com.google.devtools.common.options.InvocationPolicyEnforcer;
3638
import com.google.devtools.common.options.OptionDefinition;
3739
import com.google.devtools.common.options.OptionPriority.PriorityCategory;
40+
import com.google.devtools.common.options.OptionsBase;
3841
import com.google.devtools.common.options.OptionsParser;
3942
import com.google.devtools.common.options.OptionsParsingException;
4043
import com.google.devtools.common.options.OptionsParsingResult;
@@ -66,6 +69,14 @@ public final class BlazeOptionHandler {
6669
"client_env",
6770
"client_cwd");
6871

72+
// All options set on this pseudo command are inherited by all commands, with unrecognized options
73+
// resulting in an error.
74+
private static final String ALWAYS_PSEUDO_COMMAND = "always";
75+
76+
// All options set on this pseudo command are inherited by all commands, with unrecognized options
77+
// being ignored as long as they are recognized by at least one (other) command.
78+
private static final String COMMON_PSEUDO_COMMAND = "common";
79+
6980
// Marks an event to indicate a parsing error.
7081
static final String BAD_OPTION_TAG = "invalidOption";
7182
// Separates the invalid tag from the full error message for easier parsing.
@@ -78,6 +89,7 @@ public final class BlazeOptionHandler {
7889
private final Command commandAnnotation;
7990
private final InvocationPolicy invocationPolicy;
8091
private final List<String> rcfileNotes = new ArrayList<>();
92+
private final ImmutableList<Class<? extends OptionsBase>> allOptionsClasses;
8193

8294
BlazeOptionHandler(
8395
BlazeRuntime runtime,
@@ -92,6 +104,16 @@ public final class BlazeOptionHandler {
92104
this.commandAnnotation = commandAnnotation;
93105
this.optionsParser = optionsParser;
94106
this.invocationPolicy = invocationPolicy;
107+
this.allOptionsClasses =
108+
runtime.getCommandMap().values().stream()
109+
.map(BlazeCommand::getClass)
110+
.flatMap(
111+
cmd ->
112+
BlazeCommandUtils.getOptions(
113+
cmd, runtime.getBlazeModules(), runtime.getRuleClassProvider())
114+
.stream())
115+
.distinct()
116+
.collect(toImmutableList());
95117
}
96118

97119
/**
@@ -191,7 +213,36 @@ void parseRcOptions(
191213
"%s:\n %s'%s' options: %s",
192214
source, inherited, commandToParse, Joiner.on(' ').join(rcArgs.getArgs())));
193215
}
194-
optionsParser.parse(PriorityCategory.RC_FILE, rcArgs.getRcFile(), rcArgs.getArgs());
216+
if (commandToParse.equals(COMMON_PSEUDO_COMMAND)) {
217+
// Pass in options data for all commands supported by the runtime so that options that
218+
// apply to some but not the current command can be ignored.
219+
//
220+
// Important note: The consistency checks performed by
221+
// OptionsParser#getFallbackOptionsData ensure that there aren't any two options across
222+
// all commands that have the same name but parse differently (e.g. because one accepts
223+
// a value and the other doesn't). This means that the options available on a command
224+
// limit the options available on other commands even without command inheritance. This
225+
// restriction is necessary to ensure that the options specified on the "common"
226+
// pseudo command can be parsed unambiguously.
227+
ImmutableList<String> ignoredArgs =
228+
optionsParser.parseWithSourceFunction(
229+
PriorityCategory.RC_FILE,
230+
o -> rcArgs.getRcFile(),
231+
rcArgs.getArgs(),
232+
OptionsParser.getFallbackOptionsData(allOptionsClasses));
233+
if (!ignoredArgs.isEmpty()) {
234+
// Append richer information to the note.
235+
int index = rcfileNotes.size() - 1;
236+
String note = rcfileNotes.get(index);
237+
note +=
238+
String.format(
239+
"\n Ignored as unsupported by '%s': %s",
240+
commandAnnotation.name(), Joiner.on(' ').join(ignoredArgs));
241+
rcfileNotes.set(index, note);
242+
}
243+
} else {
244+
optionsParser.parse(PriorityCategory.RC_FILE, rcArgs.getRcFile(), rcArgs.getArgs());
245+
}
195246
}
196247
}
197248
}
@@ -227,7 +278,8 @@ private void parseArgsAndConfigs(List<String> args, ExtendedEventHandler eventHa
227278
optionsParser.parseWithSourceFunction(
228279
PriorityCategory.COMMAND_LINE,
229280
commandOptionSourceFunction,
230-
defaultOverridesAndRcSources.build());
281+
defaultOverridesAndRcSources.build(),
282+
/* fallbackData= */ null);
231283

232284
// Command-specific options from .blazerc passed in via --default_override and --rc_source.
233285
ClientOptions rcFileOptions = optionsParser.getOptions(ClientOptions.class);
@@ -241,7 +293,10 @@ private void parseArgsAndConfigs(List<String> args, ExtendedEventHandler eventHa
241293

242294
// Parses the remaining command-line options.
243295
optionsParser.parseWithSourceFunction(
244-
PriorityCategory.COMMAND_LINE, commandOptionSourceFunction, remainingCmdLine.build());
296+
PriorityCategory.COMMAND_LINE,
297+
commandOptionSourceFunction,
298+
remainingCmdLine.build(),
299+
/* fallbackData= */ null);
245300

246301
if (commandAnnotation.builds()) {
247302
// splits project files from targets in the traditional sense
@@ -372,14 +427,17 @@ void expandConfigOptions(
372427
ConfigExpander.expandConfigOptions(
373428
eventHandler,
374429
commandToRcArgs,
430+
commandAnnotation.name(),
375431
getCommandNamesToParse(commandAnnotation),
376432
rcfileNotes::add,
377-
optionsParser);
433+
optionsParser,
434+
OptionsParser.getFallbackOptionsData(allOptionsClasses));
378435
}
379436

380437
private static List<String> getCommandNamesToParse(Command commandAnnotation) {
381438
List<String> result = new ArrayList<>();
382-
result.add("common");
439+
result.add(ALWAYS_PSEUDO_COMMAND);
440+
result.add(COMMON_PSEUDO_COMMAND);
383441
getCommandNamesToParseHelper(commandAnnotation, result);
384442
return result;
385443
}
@@ -470,7 +528,9 @@ static ListMultimap<String, RcChunkOfArgs> structureRcOptionsAndConfigs(
470528
if (index > 0) {
471529
command = command.substring(0, index);
472530
}
473-
if (!validCommands.contains(command) && !command.equals("common")) {
531+
if (!validCommands.contains(command)
532+
&& !command.equals(ALWAYS_PSEUDO_COMMAND)
533+
&& !command.equals(COMMON_PSEUDO_COMMAND)) {
474534
eventHandler.handle(
475535
Event.warn(
476536
"while reading option defaults file '"

src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,8 @@ private static OptionsParsingResult parseStartupOptions(
11101110

11111111
// Then parse the command line again, this time with the correct option sources
11121112
parser = OptionsParser.builder().optionsClasses(optionClasses).allowResidue(false).build();
1113-
parser.parseWithSourceFunction(PriorityCategory.COMMAND_LINE, sourceFunction, args);
1113+
parser.parseWithSourceFunction(
1114+
PriorityCategory.COMMAND_LINE, sourceFunction, args, /* fallbackData= */ null);
11141115
return parser;
11151116
}
11161117

src/main/java/com/google/devtools/build/lib/runtime/CommonCommandOptions.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,11 @@ public String getTypeDescription() {
337337
effectTags = {OptionEffectTag.BAZEL_MONITORING},
338338
converter = MemoryProfileStableHeapParameters.Converter.class,
339339
help =
340-
"Tune memory profile's computation of stable heap at end of build. Should be two"
341-
+ " integers separated by a comma. First parameter is the number of GCs to perform."
342-
+ " Second parameter is the number of seconds to wait between GCs.")
340+
"Tune memory profile's computation of stable heap at end of build. Should be and even"
341+
+ " number of integers separated by commas. In each pair the first integer is the"
342+
+ " number of GCs to perform. The second integer in each pair is the number of"
343+
+ " seconds to wait between GCs. Ex: 2,4,4,0 would 2 GCs with a 4sec pause, followed"
344+
+ " by 4 GCs with zero second pause")
343345
public MemoryProfileStableHeapParameters memoryProfileStableHeapParameters;
344346

345347
@Option(

0 commit comments

Comments
 (0)