| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 080aa14 commit b3126b5
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -45,6 +45,7 @@ public class SpawnMetrics { | |||
| 45 | 45 | this.inputBytes = builder.inputBytes; | |
| 46 | 46 | this.inputFiles = builder.inputFiles; | |
| 47 | 47 | this.memoryEstimateBytes = builder.memoryEstimateBytes; | |
| 48 | + this.measuredMemoryPeakBytes = builder.measuredMemoryPeakBytes; | ||
| 48 | 49 | } | |
| 49 | 50 | ||
| 50 | 51 | /** Indicates whether the metrics correspond to the remote, local or worker execution. */ | |
@@ -85,6 +86,7 @@ public String toString() { | |||
| 85 | 86 | private final long inputBytes; | |
| 86 | 87 | private final long inputFiles; | |
| 87 | 88 | private final long memoryEstimateBytes; | |
| 89 | + private final long measuredMemoryPeakBytes; | ||
| 88 | 90 | ||
| 89 | 91 | /** Any non-important stats < than 10% will not be shown in the summary. */ | |
| 90 | 92 | private static final double STATS_SHOW_THRESHOLD = 0.10; | |
@@ -271,6 +273,15 @@ public long memoryEstimate() { | |||
| 271 | 273 | return memoryEstimateBytes; | |
| 272 | 274 | } | |
| 273 | 275 | ||
| 276 | + /** Measured peak memory usage in bytes, or 0 if unavailable. */ | ||
| 277 | + public long measuredMemoryPeak() { | ||
| 278 | + return measuredMemoryPeakBytes; | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + public Builder toBuilder() { | ||
| 282 | + return Builder.forExec(execKind).addDurations(this).addNonDurations(this); | ||
| 283 | + } | ||
| 284 | + | ||
| 274 | 285 | /** Limit of total size in bytes of inputs or 0 if unavailable. */ | |
| 275 | 286 | public long inputBytesLimit() { | |
| 276 | 287 | return 0; | |
@@ -317,6 +328,7 @@ public static class Builder { | |||
| 317 | 328 | private long inputBytes = 0; | |
| 318 | 329 | private long inputFiles = 0; | |
| 319 | 330 | private long memoryEstimateBytes = 0; | |
| 331 | + private long measuredMemoryPeakBytes = 0; | ||
| 320 | 332 | long inputBytesLimit = 0; | |
| 321 | 333 | long inputFilesLimit = 0; | |
| 322 | 334 | long outputBytesLimit = 0; | |
@@ -524,6 +536,12 @@ public Builder setMemoryEstimateBytes(long memoryEstimateBytes) { | |||
| 524 | 536 | return this; | |
| 525 | 537 | } | |
| 526 | 538 | ||
| 539 | + @CanIgnoreReturnValue | ||
| 540 | + public Builder setMeasuredMemoryPeakBytes(long measuredMemoryPeakBytes) { | ||
| 541 | + this.measuredMemoryPeakBytes = measuredMemoryPeakBytes; | ||
| 542 | + return this; | ||
| 543 | + } | ||
| 544 | + | ||
| 527 | 545 | @CanIgnoreReturnValue | |
| 528 | 546 | public Builder setInputBytesLimit(long inputBytesLimit) { | |
| 529 | 547 | this.inputBytesLimit = inputBytesLimit; | |
@@ -582,6 +600,7 @@ public Builder addNonDurations(SpawnMetrics metric) { | |||
| 582 | 600 | inputFiles += metric.inputFiles(); | |
| 583 | 601 | inputBytes += metric.inputBytes(); | |
| 584 | 602 | memoryEstimateBytes += metric.memoryEstimate(); | |
| 603 | + measuredMemoryPeakBytes += metric.measuredMemoryPeak(); | ||
| 585 | 604 | inputFilesLimit += metric.inputFilesLimit(); | |
| 586 | 605 | inputBytesLimit += metric.inputBytesLimit(); | |
| 587 | 606 | outputFilesLimit += metric.outputFilesLimit(); | |
@@ -596,6 +615,7 @@ public Builder maxNonDurations(SpawnMetrics metric) { | |||
| 596 | 615 | inputFiles = Long.max(inputFiles, metric.inputFiles()); | |
| 597 | 616 | inputBytes = Long.max(inputBytes, metric.inputBytes()); | |
| 598 | 617 | memoryEstimateBytes = Long.max(memoryEstimateBytes, metric.memoryEstimate()); | |
| 618 | + measuredMemoryPeakBytes = Long.max(measuredMemoryPeakBytes, metric.measuredMemoryPeak()); | ||
| 599 | 619 | inputFilesLimit = Long.max(inputFilesLimit, metric.inputFilesLimit()); | |
| 600 | 620 | inputBytesLimit = Long.max(inputBytesLimit, metric.inputBytesLimit()); | |
| 601 | 621 | outputFilesLimit = Long.max(outputFilesLimit, metric.outputFilesLimit()); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -316,10 +316,14 @@ final class SimpleSpawnResult implements SpawnResult { | |||
| 316 | 316 | this.executorHostName = builder.executorHostName; | |
| 317 | 317 | this.runnerName = builder.runnerName; | |
| 318 | 318 | this.runnerSubtype = builder.runnerSubtype; | |
| 319 | - this.spawnMetrics = | ||
| 319 | + SpawnMetrics metrics = | ||
| 320 | 320 | builder.spawnMetrics != null | |
| 321 | 321 | ? builder.spawnMetrics | |
| 322 | 322 | : SpawnMetrics.forLocalExecution(builder.wallTimeInMs); | |
| 323 | + if (builder.memoryInKb != null) { | ||
| 324 | + metrics = metrics.toBuilder().setMeasuredMemoryPeakBytes(builder.memoryInKb * 1024).build(); | ||
| 325 | + } | ||
| 326 | + this.spawnMetrics = metrics; | ||
| 323 | 327 | this.startTime = builder.startTime; | |
| 324 | 328 | this.wallTimeInMs = builder.wallTimeInMs; | |
| 325 | 329 | this.userTimeInMs = builder.userTimeInMs; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -262,6 +262,7 @@ protected static Protos.SpawnMetrics getSpawnMetricsProto(SpawnResult result) { | |||
| 262 | 262 | builder.setInputBytes(metrics.inputBytes()); | |
| 263 | 263 | builder.setInputFiles(metrics.inputFiles()); | |
| 264 | 264 | builder.setMemoryEstimateBytes(metrics.memoryEstimate()); | |
| 265 | + builder.setMeasuredMemoryPeakBytes(metrics.measuredMemoryPeak()); | ||
| 265 | 266 | builder.setInputBytesLimit(metrics.inputBytesLimit()); | |
| 266 | 267 | builder.setInputFilesLimit(metrics.inputFilesLimit()); | |
| 267 | 268 | builder.setOutputBytesLimit(metrics.outputBytesLimit()); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -114,6 +114,8 @@ message SpawnMetrics { | |||
| 114 | 114 | google.protobuf.Duration time_limit = 19; | |
| 115 | 115 | // Instant when the spawn started to execute. | |
| 116 | 116 | google.protobuf.Timestamp start_time = 20; | |
| 117 | + // Measured peak memory usage in bytes, or 0 if unavailable. | ||
| 118 | + int64 measured_memory_peak_bytes = 21; | ||
| 117 | 119 | } | |
| 118 | 120 | ||
| 119 | 121 | // Details of an executed spawn. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,7 +58,10 @@ | |||
| 58 | 58 | #include <sys/wait.h> | |
| 59 | 59 | #include <unistd.h> | |
| 60 | 60 | ||
| 61 | + #include <algorithm> | ||
| 61 | 62 | #include <atomic> | |
| 63 | + #include <cinttypes> | ||
| 64 | + #include <cstdint> | ||
| 62 | 65 | #include <string> | |
| 63 | 66 | #include <vector> | |
| 64 | 67 | ||
@@ -214,6 +217,25 @@ static pid_t SpawnPid1() { | |||
| 214 | 217 | return child_pid; | |
| 215 | 218 | } | |
| 216 | 219 | ||
| 220 | + static int64_t ReadCgroupsMemoryPeak( | ||
| 221 | + const std::vector<std::string>& cgroups_dirs) { | ||
| 222 | + int64_t max_value = -1; | ||
| 223 | + for (const std::string& dir : cgroups_dirs) { | ||
| 224 | + for (const char* fname : {"/memory.peak", "/memory.max_usage_in_bytes"}) { | ||
| 225 | + int64_t value = -1; | ||
| 226 | + std::string path = dir + fname; | ||
| 227 | + FILE* f = fopen(path.c_str(), "r"); | ||
| 228 | + if (f != nullptr) { | ||
| 229 | + if (fscanf(f, "%" SCNd64, &value) == 1) { | ||
| 230 | + max_value = std::max(value, max_value); | ||
| 231 | + } | ||
| 232 | + fclose(f); | ||
| 233 | + } | ||
| 234 | + } | ||
| 235 | + } | ||
| 236 | + return max_value; | ||
| 237 | + } | ||
| 238 | + | ||
| 217 | 239 | static int WaitForPid1(const pid_t child_pid) { | |
| 218 | 240 | // Wait for the child to exit, obtaining usage information. Restart in the | |
| 219 | 241 | // case of a signal interrupting us. | |
@@ -239,6 +261,10 @@ static int WaitForPid1(const pid_t child_pid) { | |||
| 239 | 261 | ||
| 240 | 262 | // If we're supposed to write stats to a file, do so now. | |
| 241 | 263 | if (!opt.stats_path.empty()) { | |
| 264 | + int64_t cgroups_memory_peak = ReadCgroupsMemoryPeak(opt.cgroups_dirs); | ||
| 265 | + if (cgroups_memory_peak > 0) { | ||
| 266 | + child_rusage.ru_maxrss = cgroups_memory_peak / 1024; | ||
| 267 | + } | ||
| 242 | 268 | WriteStatsToFile(&child_rusage, opt.stats_path); | |
| 243 | 269 | } | |
| 244 | 270 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,6 +32,7 @@ public void builder_addDurationsNonDurations() throws Exception { | |||
| 32 | 32 | .setInputBytes(10) | |
| 33 | 33 | .setInputFiles(20) | |
| 34 | 34 | .setMemoryEstimateBytes(30) | |
| 35 | + .setMeasuredMemoryPeakBytes(35) | ||
| 35 | 36 | .setInputBytesLimit(20) | |
| 36 | 37 | .setInputFilesLimit(40) | |
| 37 | 38 | .setOutputBytesLimit(50) | |
@@ -46,6 +47,7 @@ public void builder_addDurationsNonDurations() throws Exception { | |||
| 46 | 47 | .setInputBytes(100) | |
| 47 | 48 | .setInputFiles(200) | |
| 48 | 49 | .setMemoryEstimateBytes(300) | |
| 50 | + .setMeasuredMemoryPeakBytes(300) | ||
| 49 | 51 | .setInputBytesLimit(200) | |
| 50 | 52 | .setInputFilesLimit(400) | |
| 51 | 53 | .setOutputBytesLimit(500) | |
@@ -67,6 +69,7 @@ public void builder_addDurationsNonDurations() throws Exception { | |||
| 67 | 69 | assertThat(result.inputBytes()).isEqualTo(110); | |
| 68 | 70 | assertThat(result.inputFiles()).isEqualTo(220); | |
| 69 | 71 | assertThat(result.memoryEstimate()).isEqualTo(330); | |
| 72 | + assertThat(result.measuredMemoryPeak()).isEqualTo(335); | ||
| 70 | 73 | assertThat(result.inputBytesLimit()).isEqualTo(220); | |
| 71 | 74 | assertThat(result.inputFilesLimit()).isEqualTo(440); | |
| 72 | 75 | assertThat(result.outputBytesLimit()).isEqualTo(550); | |
@@ -84,6 +87,7 @@ public void builder_addDurationsMaxNonDurations() throws Exception { | |||
| 84 | 87 | .setInputBytes(10) | |
| 85 | 88 | .setInputFiles(20) | |
| 86 | 89 | .setMemoryEstimateBytes(30) | |
| 90 | + .setMeasuredMemoryPeakBytes(35) | ||
| 87 | 91 | .setInputBytesLimit(20) | |
| 88 | 92 | .setInputFilesLimit(40) | |
| 89 | 93 | .setOutputBytesLimit(50) | |
@@ -98,6 +102,7 @@ public void builder_addDurationsMaxNonDurations() throws Exception { | |||
| 98 | 102 | .setInputBytes(100) | |
| 99 | 103 | .setInputFiles(200) | |
| 100 | 104 | .setMemoryEstimateBytes(300) | |
| 105 | + .setMeasuredMemoryPeakBytes(300) | ||
| 101 | 106 | .setInputBytesLimit(200) | |
| 102 | 107 | .setInputFilesLimit(400) | |
| 103 | 108 | .setOutputBytesLimit(500) | |
@@ -119,6 +124,7 @@ public void builder_addDurationsMaxNonDurations() throws Exception { | |||
| 119 | 124 | assertThat(result.inputBytes()).isEqualTo(100); | |
| 120 | 125 | assertThat(result.inputFiles()).isEqualTo(200); | |
| 121 | 126 | assertThat(result.memoryEstimate()).isEqualTo(300); | |
| 127 | + assertThat(result.measuredMemoryPeak()).isEqualTo(300); | ||
| 122 | 128 | assertThat(result.inputBytesLimit()).isEqualTo(200); | |
| 123 | 129 | assertThat(result.inputFilesLimit()).isEqualTo(400); | |
| 124 | 130 | assertThat(result.outputBytesLimit()).isEqualTo(500); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -79,4 +79,18 @@ public void inMemoryContents() { | |||
| 79 | 79 | assertThat(r.getInMemoryOutput(null)).isEqualTo(null); | |
| 80 | 80 | assertThat(r.getInMemoryOutput(ActionInputHelper.fromPath("/does/not/exist"))).isEqualTo(null); | |
| 81 | 81 | } | |
| 82 | + | ||
| 83 | + @Test | ||
| 84 | + public void defaultSpawnMetricsIncludesMeasuredMemoryFromResourceUsage() { | ||
| 85 | + SpawnResult result = | ||
| 86 | + new SpawnResult.Builder() | ||
| 87 | + .setStatus(Status.SUCCESS) | ||
| 88 | + .setExitCode(0) | ||
| 89 | + .setRunnerName("test") | ||
| 90 | + .setWallTimeInMs(100) | ||
| 91 | + .setMemoryInKb(1000L) | ||
| 92 | + .build(); | ||
| 93 | + | ||
| 94 | + assertThat(result.getMetrics().measuredMemoryPeak()).isEqualTo(1000L * 1024L); | ||
| 95 | + } | ||
| 82 | 96 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2074,7 +2074,11 @@ public void testTimeout() throws Exception { | |||
| 2074 | 2074 | ||
| 2075 | 2075 | @Test | |
| 2076 | 2076 | public void testSpawnMetrics() throws Exception { | |
| 2077 | - SpawnMetrics metrics = SpawnMetrics.Builder.forLocalExec().setTotalTimeInMs(1).build(); | ||
| 2077 | + SpawnMetrics metrics = | ||
| 2078 | + SpawnMetrics.Builder.forLocalExec() | ||
| 2079 | + .setTotalTimeInMs(1) | ||
| 2080 | + .setMeasuredMemoryPeakBytes(42L) | ||
| 2081 | + .build(); | ||
| 2078 | 2082 | ||
| 2079 | 2083 | SpawnLogContext context = createSpawnLogContext(); | |
| 2080 | 2084 | ||
@@ -2093,6 +2097,7 @@ public void testSpawnMetrics() throws Exception { | |||
| 2093 | 2097 | .setMetrics( | |
| 2094 | 2098 | Protos.SpawnMetrics.newBuilder() | |
| 2095 | 2099 | .setTotalTime(millisToProto(1)) | |
| 2100 | + .setMeasuredMemoryPeakBytes(42L) | ||
| 2096 | 2101 | .setStartTime(Timestamps.fromDate(Date.from(now)))) | |
| 2097 | 2102 | .build()); | |
| 2098 | 2103 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -483,6 +483,96 @@ EOF | |||
| 483 | 483 | expect_not_log "hi there" | |
| 484 | 484 | } | |
| 485 | 485 | ||
| 486 | + function test_cgroups_v2_peak_memory_mock() { | ||
| 487 | + local local_tmp="$(mktemp -d "${OUT_DIR}/test_cgroups_v2_peak_memory_mockXXXX")" | ||
| 488 | + local stats_out_path="${local_tmp}/statsfile" | ||
| 489 | + local stats_out_decoded_path="${local_tmp}/statsfile.decoded" | ||
| 490 | + | ||
| 491 | + # Create a mock cgroup directory | ||
| 492 | + local mock_cgroup_dir="${local_tmp}/mock_cgroup" | ||
| 493 | + mkdir -p "${mock_cgroup_dir}" | ||
| 494 | + | ||
| 495 | + # Write a mock peak memory value (40MB = 41943040 bytes) | ||
| 496 | + echo "41943040" > "${mock_cgroup_dir}/memory.peak" | ||
| 497 | + | ||
| 498 | + # Run sandbox pointing to the mock cgroup directory | ||
| 499 | + local code=0 | ||
| 500 | + "${linux_sandbox}" \ | ||
| 501 | + -W "${SANDBOX_DIR}" \ | ||
| 502 | + -S "${stats_out_path}" \ | ||
| 503 | + -C "${mock_cgroup_dir}" \ | ||
| 504 | + -- /bin/true \ | ||
| 505 | + &> "${TEST_log}" || code="$?" | ||
| 506 | + | ||
| 507 | + assert_equals 0 "${code}" | ||
| 508 | + | ||
| 509 | + if ! [[ -e "${stats_out_path}" ]]; then | ||
| 510 | + fail "Stats file not found: '${stats_out_path}'" | ||
| 511 | + fi | ||
| 512 | + | ||
| 513 | + # Decode stats | ||
| 514 | + "${protoc_compiler}" --proto_path="${STATS_PROTO_DIR}" \ | ||
| 515 | + --decode tools.protos.ExecutionStatistics execution_statistics.proto \ | ||
| 516 | + < "${stats_out_path}" > "${stats_out_decoded_path}" | ||
| 517 | + | ||
| 518 | + if ! [[ -e "${stats_out_decoded_path}" ]]; then | ||
| 519 | + fail "Decoded stats file not found: '${stats_out_decoded_path}'" | ||
| 520 | + fi | ||
| 521 | + | ||
| 522 | + # Assert maxrss is reported and matches the mock value (40MB = 40960 KB) | ||
| 523 | + local maxrss=0 | ||
| 524 | + if grep -q maxrss "${stats_out_decoded_path}"; then | ||
| 525 | + maxrss="$(grep maxrss "${stats_out_decoded_path}" | cut -f2 -d':' | tr -dc '0-9')" | ||
| 526 | + fi | ||
| 527 | + | ||
| 528 | + assert_equals 40960 "${maxrss}" | ||
| 529 | + } | ||
| 530 | + | ||
| 531 | + function test_cgroups_v1_peak_memory_mock() { | ||
| 532 | + local local_tmp="$(mktemp -d "${OUT_DIR}/test_cgroups_v1_peak_memory_mockXXXX")" | ||
| 533 | + local stats_out_path="${local_tmp}/statsfile" | ||
| 534 | + local stats_out_decoded_path="${local_tmp}/statsfile.decoded" | ||
| 535 | + | ||
| 536 | + # Create a mock cgroup directory | ||
| 537 | + local mock_cgroup_dir="${local_tmp}/mock_cgroup" | ||
| 538 | + mkdir -p "${mock_cgroup_dir}" | ||
| 539 | + | ||
| 540 | + # Write a mock peak memory value (40MB = 41943040 bytes) | ||
| 541 | + echo "41943040" > "${mock_cgroup_dir}/memory.max_usage_in_bytes" | ||
| 542 | + | ||
| 543 | + # Run sandbox pointing to the mock cgroup directory | ||
| 544 | + local code=0 | ||
| 545 | + "${linux_sandbox}" \ | ||
| 546 | + -W "${SANDBOX_DIR}" \ | ||
| 547 | + -S "${stats_out_path}" \ | ||
| 548 | + -C "${mock_cgroup_dir}" \ | ||
| 549 | + -- /bin/true \ | ||
| 550 | + &> "${TEST_log}" || code="$?" | ||
| 551 | + | ||
| 552 | + assert_equals 0 "${code}" | ||
| 553 | + | ||
| 554 | + if ! [[ -e "${stats_out_path}" ]]; then | ||
| 555 | + fail "Stats file not found: '${stats_out_path}'" | ||
| 556 | + fi | ||
| 557 | + | ||
| 558 | + # Decode stats | ||
| 559 | + "${protoc_compiler}" --proto_path="${STATS_PROTO_DIR}" \ | ||
| 560 | + --decode tools.protos.ExecutionStatistics execution_statistics.proto \ | ||
| 561 | + < "${stats_out_path}" > "${stats_out_decoded_path}" | ||
| 562 | + | ||
| 563 | + if ! [[ -e "${stats_out_decoded_path}" ]]; then | ||
| 564 | + fail "Decoded stats file not found: '${stats_out_decoded_path}'" | ||
| 565 | + fi | ||
| 566 | + | ||
| 567 | + # Assert maxrss is reported and matches the mock value (40MB = 40960 KB) | ||
| 568 | + local maxrss=0 | ||
| 569 | + if grep -q maxrss "${stats_out_decoded_path}"; then | ||
| 570 | + maxrss="$(grep maxrss "${stats_out_decoded_path}" | cut -f2 -d':' | tr -dc '0-9')" | ||
| 571 | + fi | ||
| 572 | + | ||
| 573 | + assert_equals 40960 "${maxrss}" | ||
| 574 | + } | ||
| 575 | + | ||
| 486 | 576 | # The test shouldn't fail if the environment doesn't support running it. | |
| 487 | 577 | is_linux || exit 0 | |
| 488 | 578 | check_sandbox_allowed || exit 0 | |
| Back | FazBrowse Home | New Git URL |
0 commit comments