The Looppoint checkpoint feature is still under-development.
These tests are stalling indefinitely and causing the Weekly tests to
timeout. They are therefore disabled until looppoint functionality is
complete and this bug is resolved.
Change-Id: I2c16abdaec639120c0ba349d90f5cc2fe747f8e6
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69240
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
The script run by 'test_replacement_policies.py' was named
'run_replacement_policy_test.py'. This caused the TestLib framework to
fail as any file with the string "test" in it was attempted to parse as
a test. As this is a gem5 Python config file, this caused a failure.
To fix this the file was renamed 'run_replacement_policy.py'.
Change-Id: I6f0a09e4f5056a9c94e258c9aea26bf277206733
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69237
Reviewed-by: Matt Sinclair <mattdsinclair@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Matt Sinclair <mattdsinclair@gmail.com>
The current GPU Ruby Random tester tests only test for WT L2 caches,
meaning that some transitions (specific to WB caches) are never
tested. To help ensure better coverage, this commit adds a separate
test that tests WB GPU L2 caches to the per-checkin and nightly
regressions.
Change-Id: I539ece3b825b9a38630027d947dc11ebef588752
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69258
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
By default the GPU VIPER coherence protocol uses a WT L2 cache.
However it has support for using WB caches (although this is not
tested currently). When using a WB L2 cache for the GPU, this
results in deadlocks with atomics.
Specifically, when an atomic reaches the L2 and the line is
currently in M or W, the line must be written back before the atomic
can be performed. However, the current support has two issues:
a) it never performs the atomic operation -- while VIPER current
assumes all atomics are system scope atomics and thus cannot be
performed at the L2 and this transition requires the dirty line be
written back before performing the atomic, the transition never
performs the atomic nor does the response path handle it.
b) putting the atomic action right after the write back is not
safe because we need to ensure the requests are ordered when they
reach memory -- thus we have to wait until the write back is
acknowledged before it's safe to send/perform the atomic.
To fix this, this change modifies the transition in question to
put the atomic on the stalled requests buffer, which the WBAck will
check when it returns to the L2 (and thus perform the atomic, which
will result in the atomic being sent on to the directory).
This fix has been tested and verified with both the per-checkin and
nightly GPU Ruby Random tester tests (with a WB L2 cache).
Change-Id: I9a43fd985dc71297521f4b05c47288d92c314ac7
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/68978
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Matthew Poremba <matthew.poremba@amd.com>
Tested-by: kokoro <noreply+kokoro@google.com>
By default the GPU VIPER coherence protocol uses a WT L2 cache.
However it has support for using WB caches (although this is not
tested currently). When using a WB L2 cache for the GPU, this
results in deadlocks with loads.
Specifically, when a load reaches the L2 and the line is currently
in the W state, that line must be written back before the load can
be performed. However, the current transition for this in the L2
did not attempt to retry the load when the WB completes, resulting
in a deadlock. This deadlock can be replicated by running the GPU
Ruby random tester as is with a WB L2 cache instead of a WT L2
cache.
To fix this, this change modifies the transition in question to
put the load on the stalled requests buffer, which the WBAck will
check when it returns to the L2 (and thus perform the load).
This fix has been tested and verified with both the per-checkin and
nightly GPU Ruby Random tester tests (with a WB L2 cache).
Change-Id: Ieec4f61a3070cf9976b8c3ef0cdbd0cc5a1443c6
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/68977
Reviewed-by: Matthew Poremba <matthew.poremba@amd.com>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
This option was almost always overridden from false to true anyway,
except in one place (in the ethertap device) which was likely just by
accident.
This will give external users a chance to remove the option without
changing behavior, so that the option can be removed entirely in a
later change.
Change-Id: I77add40b8131b91997b2aecbfff6c7de0ee9ead9
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69157
Reviewed-by: Yu-hsin Wang <yuhsingw@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
The DEBUG macro is not part of any compiler standards (differently from
NDEBUG, which elides assertions).
It is only meant to differentiate gem5.debug from .fast and .opt builds.
gem5 developers have used it to insert helper code that is supposed to
aid the debugging process in case anything goes wrong.
This generic name is likely to clash with other libraries linked with
gem5. This is the case of DRAMSim as an example.
Rather than using undef tricks, we just inject a GEM5_DEBUG macro
for gem5.debug builds.
Change-Id: Ie913ca30da615bd0075277a260bbdbc397b7ec87
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69079
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
The static method is only present in gem5.debug builds and
it doesn't seem to do anything else than setting up a boolean
flag. I was not able to understand from git history which
was the purpose of it but I am confident it's not currently
used.
It was maybe meant to work with the --debug-break option, which
sends a SIGRAP to the current PID in order to halt a GDB
session at a specific time. Maybe the idea was to halt only
when executing a SimObject method?
Change-Id: Ic21e8f55b5e35480c2bc617c24e1352c37516e8f
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69078
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
The fastmodel simulation would be paused when it hits a breakpoint.
However, the order of stop event happens after the breakpoint event. If
we handle the breakpoint logic in the breakpoint event, it may cause
somehow status unsynchronized. To make the behavior stable, we delay the
breakpoint handle until the simulation stop event called.
Change-Id: I0083561f561af71370ccaa066220b72ed7831b78
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/68697
Reviewed-by: Earl Ou <shunhsingou@google.com>
Maintainer: Gabe Black <gabe.black@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This new event class simplifies the use of EventWrapper and aims at
superseeding it. EventWrapper has been redefined in terms of
MemberEventWrapper.
MemberEventWrapper makes use of the new type traits to simplify
template parameterization and encourage its use over SimpleEvent that
often wraps a lambda that merely calls a member function.
Change-Id: Ie59e4c51705b9c2b2faa27097678d7d85f5b99c6
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67653
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Daniel Carvalho <odanrc@yahoo.com.br>
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
We have a setup that requires manual startup of an ssh proxy to
access license server, and without that, gem5 takes about a minute
until the license checkout times out (until then, it's unclear
why nothing is happening).
We asked ARM for a way to decrease timeouts, but that doesn't
seem to be easy to do.
Change-Id: I37b84fd52cb7fb221a9e48dcb52a33a11f4d1580
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/68177
Reviewed-by: Gabe Black <gabe.black@gmail.com>
Maintainer: Gabe Black <gabe.black@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
These all look like valid (but harmless) diagnostics to me and are
all simple to fix. Most of them can be fixed by using ASSERT_* variants
of the GTest checkers to ensure that the remainder of the function is
not executed and the uninitialized result isn't touched.
Change-Id: Ib5fe2ac2ec539c880d670ebc3321ce98940c7e38
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/68517
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
The weekly test script was implicitly assuming that no m5out
directory existed in the folder where the script was run.
However, if a prior test ran and failed, it would not clean up
its m5out directory, causing the weekly tests to fail.
This commit resolves this by removing the m5out directory before
trying to run any tests in the weekly script. Moreover, we also
update the weekly script to explicitly remove this m5out directory
at the end of the script.
Change-Id: If10c59034528e171cc2c5dacb928b3a81d6b8c50
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67198
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
The lock feature will let M mode do memory permission check before
R/W/X data. If the lock bit of pmpicfg set, then the pmpicfg and
pmpaddri will ignore the update value later until CPU reset, and
pmpaddri-1 will ignore if the TOR A field is set.
The following is add in CL:
1. Add condition to run PMP check when any lock bit of pmp tables
is set
2. Add PMP_LOCK bit check when try to update pmpaddr and pmpcfg
3. If there is no PMP entry matches and priviledge mode is M,
no fault generated
4. If the address matches PMP entry, return no fault if priviledge
mode is M and lock bit is not set
For more details about PMP, please see RISC-V Spec Volumn II,
Priviledge Archtecture, Ver 1.12, Section 3.7 Physical Memory
Protection
Change-Id: I3e7c5824d6c05f2ea928ee9ec7714f7271e4c58c
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/68057
Reviewed-by: Ayaz Akram <yazakram@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Yu-hsin Wang <yuhsingw@google.com>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
The stats moved are from fetch.hh and fetch.cc of O3. Stat branches is
now tracked by numBranches. Stat branchRate is now tracked by
branchRate in FetchCPUStats. Stat rate is tracked by fetchRate. Stat
insts is tracked by numInsts. Stat icacheStallCycles is tracked by
icacheStallCycles in FetchCPUStats.
Change-Id: I48313614edd078631df4ef6b00982c335798fcb1
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67394
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Moved committedInsts from O3 cpu.* to BaseCPU as numInstsNotNOP because
it tracks the instructions committed that are not NOPs or prefetches.
This change also does the same for commitedOps. InstsCommitted from O3
commit.*, which tracks all instructions committed, has been removed.
CommitCPUStats::numInsts replaces it in O3. The same has been done for
opsCommitted. Because IPC and CPI calculations are handled in BaseCPU,
removed IPC and CPI stats from O3 cpu.*.
Change-Id: I9f122c9a9dafccd5342f18056f282f3dad8b1b1e
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67393
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
In BaseCPU::BaseCPUStats, numInsts and numOps track per CPU core
committed instructions and operations.
In BaseCPU::FetchCPUStats, numInsts and numOps track per thread
fetched instructions and operations.
In BaseCPU::CommitCPUStats, numInsts and numOps track per thread
committed instructions and operations.
In BaseSimpleCPU, the countInst() function has been split into
countInst(), countFetchInst(), and countCommitInst(). The stat count
incrementation of countInst() has been removed and delegated to the
other two functions. countFetchInst() increments numInsts and numOps
of the FetchCPUStats group for a thread. countCommitInst() increments
the numInsts and numOps of the CommitCPUStats group for a thread and
of the BaseCPUStats group for a CPU core. These functions are called
in the appropriate stage within timing.cc and atomic.cc. The call to
countInst() is left unchanged. countFetchInst() is called in
preExecute(). countCommitInst() is called in postExecute().
For MinorCPU, only the commit level numInsts and numOps stats have been
implemented.
IPC and CPI stats have been added to BaseCPUStats (core level) and
CommitCPUStats (thread level). The formulas for the IPC and CPI stats
in CommitCPUStats are set in the BaseCPU constructor, after the
CommitCPUStats stat group object has been created.
Change-Id: If893b331fe4a6908e4b4caf4a30f1b0aeb4c4266
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67392
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Created stat group CommitCPUStats in BaseCPU and moved stats from the
simple cpu model.
The stats moved from SImpleCPU are numCondCtrlInsts, numFpInsts,
numIntInsts, numLoadInsts, numStoreInsts, numVecInsts.
Moved committedControl of MinorCPU to BaseCPU::CommittedCPUStats. In
MinorCPU, this stat was a 2D vector, where the first dimension is the
thread ID. In base it is now a 1D vector that is tied to a thread ID
via the commitStats vector.
The committedControl stat vector in CommitCPUStats is updated in the
same way in all CPU models. The function updateComCtrlStats will
update committedControl and the CPU models will call this function
instead of updating committedControl directly. This function takes
a StaticInstPtr as input, which Simple, Minor, and O3 CPU models are
able to provide.
Removed stat "branches" from O3 commit stage. This stat duplicates
BaseCPU::CommittedCPUStats::committedControl::IsControl.
O3 commit stats floating, integer, loads, memRefs, vectorInstructions
are replaced by numFpInsts, numIntInsts, numLoadInsts, numMemRefs,
numVecInsts from BaseCPU::CommitCPUStats respectively. Implemented
numStoreInsts from BaseCPU::commitCPUStats for O3 commit stage.
Change-Id: I362cec51513a404de56a02b450d7663327be20f5
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67391
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Created stat group ExecuteCPUStats in BaseCPU and moved stats from the
simple and minor cpu models.
The stats moved from SimpleCPU are dcacheStallCycles,
icacheStallCycles, numCCRegReads, numCCRegWrites, numFpAluAccesses,
numFpRegReads, numFpRegWrites, numIntAluAccesses, numIntRegReads,
numIntRegWrites, numMemRefs, numMiscRegReads, numMiscRegWrites,
numVecAluAccesses, numVecPredRegReads, numVecPredRegWrites,
numVecRegReads, numVecRegWrites.
The stat moved from MinorCPU is numDiscardedOps.
Also, ccRegfileReads, ccRegfileWrites, fpRegfileReads, fpRegfileWrites,
intRegfileReads, intRegfileWrites, miscRegfileReads, miscRegfileWrites,
vecPredRegfileReads, vecPredRegfileWrites, vecRegfileReads,
and vecRegfileWrites are removed from cpu.hh and cpu.cc in O3CPU. The
corresponding stats in BaseCPU::ExecuteCPUStats are used instead.
Changed the getReg, getWritableReg, and setReg functions in the O3 CPU
object to take the thread ID as a parameter. This is because the stats
in base are stored in vectors that are indexed by thread ID.
Change-Id: I801c5ceb4c70b7b281127569f11c6ee98f614b27
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67390
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
This summarizes a series of changes to move general Simple, Minor,
O3 CPU stats to BaseCPU. This commit focuses on moving numBranches
from SimpleCPU to the FetchCPUStats in the BaseCPU, and
numFetchSuspends from MinorCPU into FetchCPUStats. More general
information about this relation chain is below
1. Summary:
Moved general CPU stats found across Simple, Minor, and O3 CPU models
into BaseCPU through new stat groups. The stat groups are
FetchCPUStats, ExecuteCPUStats, and CommitCPUStats. Implemented the
committedControl stat vector found in MinorCPU for Simple and O3 CPU.
Implemented the numStoreInsts stat found in SimpleCPU for O3CPU. IPC
and CPI stats are now tracked at the core and thread level in BaseCPU
and are made universal for simple, minor, o3, and kvm CPUs. Duplicate
stats across the models are merged into a single stat in BaseCPU under
the same stat name. This change does not implement every general level
stat moved to BaseCPU for every model.
2. Stat API Changes
a. SimpleCPU:
statExecutedInstType vector unified into committedInstType
numCondCtrlInsts unified into committedControl::isControl
b. O3CPU:
i. Fetch Stage
branches in fetch unified into with numBranches
rate renamed to fetchRate
insts unified into with numInsts
ii. Execute Stage
Regfile stats unified into base with use of Simple's stat naming
numRefs in IEW unified into numMemRefs
numRate from IEW renamed to instRate
iii. Commit Stage
committedInsts is renamed to numInstsNotNOP
committedOps is renamed to numOpsNotNOP
instsCommitted is unified into numInsts
opsCommitted is unified into numOps
branches is unified into committedControl::isControl
floating is unified into numFpInsts
integer is unified into numIntInsts
loads is unified into numLoadInsts
memRefs is renamed to numMemRefs
vectorInstructions is unified into numVecInsts
3. Details:
Created three stat groups in BaseCPU. FetchCPUStats track statistics
related to the fetch stage. ExecuteCPUStats track statistics related
to the execute stage. CommitCPUStats track statistics related to the
commit stage.
There are three vectors in Base that store unique pointers to per
thread instances of these stat groups. The stat group pointer for
thread i is accessible at index i of one of these vectors. For example,
stat numCCRegReads of the execute stage for thread 0 can be accessed
with executeStats[0]->numCCRegReads. The stats.txt output will print the
thread ID of the stat group. For example, numVecRegReads on thread 0
of a single core prints as
"board.processor.cores.core.executeStats0.numVecRegReads".
NOTE: Multithreading in gem5 is untested. Therefore per thread stats
output in stats.txt is not currently guaranteed to be correctly
formatted.
For FetchCPUStats, the stats moved from SimpleCPU are numBranches
and numInsts. From MinorCPU, the stat moved is numFetchSuspends. From
O3CPU, the stats moved are from the O3 fetch stage: Stat branches is
unified into numBranches, stat rate is renamed to fetchRate in Base,
stat insts is unified into numInsts, stat icacheStallCycles keeps the
same name in Base.
For ExecuteCPUStats, the stats moved from SimpleCPU are
dcacheStallCycles, numCCRegReads, numCCRegWrites,
numFpAluAccesses, numFpRegReads, numFpRegWrites, numIntAluAccesses,
numIntRegReads, numIntRegWrites, numMemRefs, numMiscRegReads,
numMiscRegWrites, numVecAluAccesses, numVecPredRegReads,
numVecPredRegWrites, numVecRegReads, numVecRegWrites. The stat moved
from MinorCPU is numDiscardedOps. From O3, the Regfile stats in CPU are
unified into the reg stats in Base and use the names found originally
in SimpleCPU. From O3 IEW stage, numInsts keeps the same name in
Base, numBranches is unified into numBranches in base, numNop keeps
the same name in Base, numRefs is unified into numMemRefs in Base,
numLoadInsts and numStoreInsts are moved into Base, numRate is renamed
to instRate in base.
For CommitCPUStats, the stats moved from SimpleCPU are
numCondCtrlInsts, numFpInsts, numIntInsts, numLoadInsts, numStoreInsts,
numVecInsts. The stats moved from MinorCPU are numInsts,
committedInstType, and committedControl. statExecutedInstType of
SimpleCPU is unified with committedInstType of MinorCPU. Implemented
committedControl stats from MinorCPU in Simple and O3 CPU. In MinorCPU,
this stat was a 2D vector, where the first dimension is the thread ID.
In base it is now a 1D vector that is tied to a thread ID via the
commitStats vector that the object is accessible through. From the O3
commit stage, committedInsts is renamed to numInstsNotNOP, committedOps
is renamed to numOpsNotNOP, instsCommitted is unified into numInsts,
opsCommitted is renamed to numOps, committedInstType is unified into
committedInstType from Minor, branches is removed because it duplicates
committedControl::IsControl, floating is unified into numFpInsts,
interger is unified into numIntInsts, loads is unified into
numLoadInsts, numStoreInsts is implemented for tracking in O3, memRefs
is renamed to numMemRefs, vectorInstructions is unified into
numVecInsts. Note that numCondCtrlInsts of Simple is unified into
committedControl::IsCondCtrl.
Implemented IPC and CPI tracking inside BaseCPU.
In BaseCPU::BaseCPUStats, numInsts and numOps track per CPU core
committed instructions and operations.
In BaseCPU::FetchCPUStats, numInsts and numOps track per thread
fetched instructions and operations.
In BaseCPU::CommitCPUStats, numInsts tracks per thread executed
instructions.
In BaseCPU::CommitCPUStats, numInsts and numOps track per thread
committed instructions and operations.
In BaseSimpleCPU, the countInst() function has been split into
countInst(), countFetchInst(), and countCommitInst(). The stat count
incrementation step of countInst() has been removed and delegated to the
other two functions. countFetchInst() increments numInsts and numOps
of the FetchCPUStats group for a thread. countCommitInst() increments
the numInsts and numOps of the CommitCPUStats group for a thread and
of the BaseCPUStats group for a CPU core. These functions are called
in the appropriate stage within timing.cc and atomic.cc. The call to
countInst() is left unchanged. countFetchInst() is called in
preExecute(). countCommitInst() is called in postExecute().
For MinorCPU, only the commit level numInsts and numOps stats have been
implemented.
IPC and CPI stats have been added to BaseCPUStats (core level) and
CommitCPUStats (thread level). The formulas for the IPC and CPI stats
in CommitCPUStats are set in the BaseCPU constructor, after the
CommitCPUStats stat group object has been created. These replace IPC,
CPI, totalIpc, and totalCpi stats in O3.
Replaced committedInsts stats of KVM CPU with commitStats.numInsts
of BaseCPU. This results in IPC and CPI printing in stats.txt for
KVM simulations.
This change does not implement most general stats found in one or two
model for all others.
Jira Ticket: https://gem5.atlassian.net/browse/GEM5-1304
Change-Id: I3c852f8dba3268c71b7a3415480fb63d8dc30cb7
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/66031
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>