tests,configs,mem-ruby: Adding Ruby tester for GPU_VIPER
This patch adds the GPU protocol tester that uses data-race-free operation to discover bugs in GPU protocols including GPU_VIPER. For more information please see the following paper and the README: T. Ta, X. Zhang, A. Gutierrez and B. M. Beckmann, "Autonomous Data-Race-Free GPU Testing," 2019 IEEE International Symposium on Workload Characterization (IISWC), Orlando, FL, USA, 2019, pp. 81-92, doi: 10.1109/IISWC47752.2019.9042019. Change-Id: Ic9939d131a930d1e7014ed0290601140bdd1499f Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/32855 Reviewed-by: Matt Sinclair <mattdsinclair@gmail.com> Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Maintainer: Matt Sinclair <mattdsinclair@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
321
src/cpu/testers/gpu_ruby_test/episode.cc
Normal file
321
src/cpu/testers/gpu_ruby_test/episode.cc
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2020 Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* For use for simulation and test purposes only
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cpu/testers/gpu_ruby_test/episode.hh"
|
||||
|
||||
#include <fstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "cpu/testers/gpu_ruby_test/gpu_thread.hh"
|
||||
#include "cpu/testers/gpu_ruby_test/protocol_tester.hh"
|
||||
|
||||
Episode::Episode(ProtocolTester* _tester, GpuThread* _thread, int num_loads,
|
||||
int num_stores)
|
||||
: tester(_tester),
|
||||
thread(_thread),
|
||||
numLoads(num_loads),
|
||||
numStores(num_stores),
|
||||
nextActionIdx(0)
|
||||
{
|
||||
assert(tester && thread);
|
||||
|
||||
episodeId = tester->getNextEpisodeID();
|
||||
numLanes = thread->getNumLanes();
|
||||
assert(numLanes > 0);
|
||||
|
||||
addrManager = tester->getAddressManager();
|
||||
assert(addrManager);
|
||||
|
||||
atomicLocs.resize(numLanes, AddressManager::INVALID_LOCATION);
|
||||
// generate a sequence of actions
|
||||
initActions();
|
||||
isActive = true;
|
||||
|
||||
DPRINTFN("Episode %d\n", episodeId);
|
||||
}
|
||||
|
||||
Episode::~Episode()
|
||||
{
|
||||
for (Episode::Action* action : actions) {
|
||||
assert(action);
|
||||
delete action;
|
||||
}
|
||||
}
|
||||
|
||||
const Episode::Action*
|
||||
Episode::peekCurAction() const
|
||||
{
|
||||
if (nextActionIdx < actions.size())
|
||||
return actions[nextActionIdx];
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Episode::popAction()
|
||||
{
|
||||
assert(nextActionIdx < actions.size());
|
||||
nextActionIdx++;
|
||||
}
|
||||
|
||||
void
|
||||
Episode::initActions()
|
||||
{
|
||||
// first, push Atomic & then Acquire action
|
||||
actions.push_back(new Action(Action::Type::ATOMIC, numLanes));
|
||||
actions.push_back(new Action(Action::Type::ACQUIRE, numLanes));
|
||||
|
||||
// second, push a number of LD/ST actions
|
||||
int num_loads = numLoads;
|
||||
int num_stores = numStores;
|
||||
while ((num_loads + num_stores) > 0) {
|
||||
switch (random() % 2) {
|
||||
case 0: // Load
|
||||
if (num_loads > 0) {
|
||||
actions.push_back(new Action(Action::Type::LOAD,
|
||||
numLanes));
|
||||
num_loads--;
|
||||
}
|
||||
break;
|
||||
case 1: // Store
|
||||
if (num_stores > 0) {
|
||||
actions.push_back(new Action(Action::Type::STORE,
|
||||
numLanes));
|
||||
num_stores--;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// last, push an Release & then Atomic action
|
||||
actions.push_back(new Action(Action::Type::RELEASE, numLanes));
|
||||
actions.push_back(new Action(Action::Type::ATOMIC, numLanes));
|
||||
|
||||
// for each lane, pick a list of locations
|
||||
Location normal_loc;
|
||||
|
||||
for (int lane = 0; lane < numLanes; ++lane) {
|
||||
normal_loc = AddressManager::INVALID_LOCATION;
|
||||
|
||||
// first, we select atomic loc for this lane
|
||||
// atomic loc for this lane should not have been picked yet
|
||||
assert(atomicLocs[lane] == AddressManager::INVALID_LOCATION);
|
||||
// pick randomly an atomic location
|
||||
atomicLocs[lane] = addrManager->getAtomicLoc();
|
||||
assert(atomicLocs[lane] >= 0);
|
||||
|
||||
// go through each action in this lane and set its location
|
||||
for (Action* action : actions) {
|
||||
assert(action);
|
||||
|
||||
switch (action->getType()) {
|
||||
case Action::Type::ATOMIC:
|
||||
action->setLocation(lane, atomicLocs[lane]);
|
||||
break;
|
||||
case Action::Type::LOAD:
|
||||
// pick randomly a normal location
|
||||
normal_loc = addrManager->
|
||||
getLoadLoc(atomicLocs[lane]);
|
||||
assert(normal_loc >= AddressManager::INVALID_LOCATION);
|
||||
|
||||
if (normal_loc != AddressManager::INVALID_LOCATION) {
|
||||
// check DRF
|
||||
if (!tester->checkDRF(atomicLocs[lane],
|
||||
normal_loc, false) ||
|
||||
!this->checkDRF(atomicLocs[lane], normal_loc,
|
||||
false, lane)) {
|
||||
panic("GpuTh %d - Data race detected. STOPPED!\n",
|
||||
thread->getGpuThreadId());
|
||||
}
|
||||
}
|
||||
|
||||
action->setLocation(lane, normal_loc);
|
||||
break;
|
||||
case Action::Type::STORE:
|
||||
// pick randomly a normal location
|
||||
normal_loc = addrManager->
|
||||
getStoreLoc(atomicLocs[lane]);
|
||||
assert(normal_loc >= AddressManager::INVALID_LOCATION);
|
||||
|
||||
if (normal_loc != AddressManager::INVALID_LOCATION) {
|
||||
// check DRF
|
||||
if (!tester->checkDRF(atomicLocs[lane],
|
||||
normal_loc, true) ||
|
||||
!this->checkDRF(atomicLocs[lane], normal_loc,
|
||||
true, lane)) {
|
||||
panic("GpuTh %d - Data race detected. STOPPED!\n",
|
||||
thread->getGpuThreadId());
|
||||
}
|
||||
}
|
||||
|
||||
action->setLocation(lane, normal_loc);
|
||||
break;
|
||||
case Action::Type::ACQUIRE:
|
||||
case Action::Type::RELEASE:
|
||||
// no op
|
||||
break;
|
||||
default:
|
||||
panic("Invalid action type\n");
|
||||
}
|
||||
}
|
||||
|
||||
addrManager->finishLocSelection(atomicLocs[lane]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Episode::completeEpisode()
|
||||
{
|
||||
// release all locations this episode has picked and used
|
||||
Location atomic_loc, normal_loc;
|
||||
for (int lane = 0; lane < numLanes; ++lane) {
|
||||
atomic_loc = AddressManager::INVALID_LOCATION;
|
||||
normal_loc = AddressManager::INVALID_LOCATION;
|
||||
|
||||
std::unordered_set<Location> unique_loc_set;
|
||||
|
||||
for (Action* action : actions) {
|
||||
assert(action);
|
||||
|
||||
if (action->isAtomicAction()) {
|
||||
if (atomic_loc == AddressManager::INVALID_LOCATION) {
|
||||
atomic_loc = action->getLocation(lane);
|
||||
} else {
|
||||
// both atomic ops in the same lane must be
|
||||
// at the same location
|
||||
assert(atomic_loc == action->getLocation(lane));
|
||||
}
|
||||
} else if (!action->isMemFenceAction()) {
|
||||
assert(atomic_loc >= 0);
|
||||
normal_loc = action->getLocation(lane);
|
||||
|
||||
if (normal_loc >= 0)
|
||||
unique_loc_set.insert(normal_loc);
|
||||
}
|
||||
}
|
||||
|
||||
// each unique loc can be released only once
|
||||
for (Location loc : unique_loc_set)
|
||||
addrManager->releaseLocation(atomic_loc, loc);
|
||||
}
|
||||
|
||||
// this episode is no longer active
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
bool
|
||||
Episode::checkDRF(Location atomic_loc, Location loc, bool isStore,
|
||||
int max_lane) const
|
||||
{
|
||||
assert(atomic_loc != AddressManager::INVALID_LOCATION);
|
||||
assert(loc != AddressManager::INVALID_LOCATION);
|
||||
assert(max_lane <= numLanes);
|
||||
|
||||
for (int lane = 0; lane < max_lane; ++lane) {
|
||||
if (atomic_loc == atomicLocs[lane]) {
|
||||
for (const Action* action : actions) {
|
||||
if (!action->isAtomicAction() &&
|
||||
!action->isMemFenceAction()) {
|
||||
if (isStore && loc == action->getLocation(lane)) {
|
||||
warn("ST at location %d races against thread %d\n",
|
||||
loc, thread->getGpuThreadId());
|
||||
return false;
|
||||
} else if (!isStore &&
|
||||
action->getType() == Action::Type::STORE &&
|
||||
loc == action->getLocation(lane)) {
|
||||
warn("LD at location %d races against thread %d\n",
|
||||
loc, thread->getGpuThreadId());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------- Action class ----------------------------
|
||||
Episode::Action::Action(Type t, int num_lanes)
|
||||
: type(t),
|
||||
numLanes(num_lanes)
|
||||
{
|
||||
assert(numLanes > 0);
|
||||
locations.resize(numLanes);
|
||||
for (Location &loc : locations) loc = AddressManager::INVALID_LOCATION;
|
||||
}
|
||||
|
||||
void
|
||||
Episode::Action::setLocation(int lane, Location loc)
|
||||
{
|
||||
assert(lane >= 0 && lane < numLanes);
|
||||
locations[lane] = loc;
|
||||
}
|
||||
|
||||
AddressManager::Location
|
||||
Episode::Action::getLocation(int lane) const
|
||||
{
|
||||
assert(lane >= 0 && lane < numLanes);
|
||||
return locations[lane];
|
||||
}
|
||||
|
||||
bool
|
||||
Episode::Action::isAtomicAction() const
|
||||
{
|
||||
return (type == Type::ATOMIC);
|
||||
}
|
||||
|
||||
bool
|
||||
Episode::Action::isMemFenceAction() const
|
||||
{
|
||||
return (type == Type::ACQUIRE || type == Type::RELEASE);
|
||||
}
|
||||
|
||||
const std::string
|
||||
Episode::Action::printType() const
|
||||
{
|
||||
if (type == Type::ACQUIRE)
|
||||
return "ACQUIRE";
|
||||
else if (type == Type::RELEASE)
|
||||
return "RELEASE";
|
||||
else if (type == Type::ATOMIC)
|
||||
return "ATOMIC";
|
||||
else if (type == Type::LOAD)
|
||||
return "LOAD";
|
||||
else if (type == Type::STORE)
|
||||
return "STORE";
|
||||
else
|
||||
panic("Invalid action type\n");
|
||||
}
|
||||
Reference in New Issue
Block a user