11388SN/A/* 211359Sandreas@sandberg.pp.se * Copyright (c) 2015 ARM Limited 311359Sandreas@sandberg.pp.se * All rights reserved 411359Sandreas@sandberg.pp.se * 511359Sandreas@sandberg.pp.se * The license below extends only to copyright in the software and shall 611359Sandreas@sandberg.pp.se * not be construed as granting a license to any other intellectual 711359Sandreas@sandberg.pp.se * property including but not limited to intellectual property relating 811359Sandreas@sandberg.pp.se * to a hardware implementation of the functionality of the software 911359Sandreas@sandberg.pp.se * licensed hereunder. You may use the software subject to the license 1011359Sandreas@sandberg.pp.se * terms below provided that you ensure that this notice is replicated 1111359Sandreas@sandberg.pp.se * unmodified and in its entirety in all distributions of the software, 1211359Sandreas@sandberg.pp.se * modified or unmodified, in source code or in binary form. 1311359Sandreas@sandberg.pp.se * 1411359Sandreas@sandberg.pp.se * Copyright (c) 2013 Andreas Sandberg 151388SN/A * Copyright (c) 2005 The Regents of The University of Michigan 161388SN/A * All rights reserved. 171388SN/A * 181388SN/A * Redistribution and use in source and binary forms, with or without 191388SN/A * modification, are permitted provided that the following conditions are 201388SN/A * met: redistributions of source code must retain the above copyright 211388SN/A * notice, this list of conditions and the following disclaimer; 221388SN/A * redistributions in binary form must reproduce the above copyright 231388SN/A * notice, this list of conditions and the following disclaimer in the 241388SN/A * documentation and/or other materials provided with the distribution; 251388SN/A * neither the name of the copyright holders nor the names of its 261388SN/A * contributors may be used to endorse or promote products derived from 271388SN/A * this software without specific prior written permission. 281388SN/A * 291388SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 301388SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 311388SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 321388SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 331388SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 341388SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 351388SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 361388SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 371388SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 381388SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 391388SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 402665Ssaidi@eecs.umich.edu * 412665Ssaidi@eecs.umich.edu * Authors: Nathan Binkert 428634Schris.emmons@arm.com * Chris Emmons 4311359Sandreas@sandberg.pp.se * Andreas Sandberg 4411359Sandreas@sandberg.pp.se * Sascha Bischoff 451388SN/A */ 461388SN/A 4711793Sbrandon.potter@amd.com#include "base/output.hh" 4811793Sbrandon.potter@amd.com 4911793Sbrandon.potter@amd.com#include <dirent.h> 501388SN/A#include <sys/stat.h> 511388SN/A#include <sys/types.h> 5210810Sbr@bsdpad.com#include <unistd.h> 5311793Sbrandon.potter@amd.com#include <zfstream.h> 541388SN/A 558634Schris.emmons@arm.com#include <cassert> 568229Snate@binkert.org#include <cerrno> 578229Snate@binkert.org#include <climits> 588229Snate@binkert.org#include <cstdlib> 591388SN/A#include <fstream> 601388SN/A 6112334Sgabeblack@google.com#include "base/logging.hh" 621388SN/A 631388SN/Ausing namespace std; 641388SN/A 651388SN/AOutputDirectory simout; 661388SN/A 6711359Sandreas@sandberg.pp.se 6811359Sandreas@sandberg.pp.seOutputStream::OutputStream(const std::string &name, std::ostream *stream) 6911359Sandreas@sandberg.pp.se : _name(name), _stream(stream) 7011359Sandreas@sandberg.pp.se{ 7111359Sandreas@sandberg.pp.se} 7211359Sandreas@sandberg.pp.se 7311359Sandreas@sandberg.pp.seOutputStream::~OutputStream() 7411359Sandreas@sandberg.pp.se{ 7511359Sandreas@sandberg.pp.se} 7611359Sandreas@sandberg.pp.se 7711359Sandreas@sandberg.pp.sevoid 7811359Sandreas@sandberg.pp.seOutputStream::relocate(const OutputDirectory &dir) 7911359Sandreas@sandberg.pp.se{ 8011359Sandreas@sandberg.pp.se} 8111359Sandreas@sandberg.pp.se 8211359Sandreas@sandberg.pp.setemplate<class StreamType> 8311359Sandreas@sandberg.pp.seOutputFile<StreamType>::OutputFile(const OutputDirectory &dir, 8411359Sandreas@sandberg.pp.se const std::string &name, 8511359Sandreas@sandberg.pp.se std::ios_base::openmode mode, 8611359Sandreas@sandberg.pp.se bool recreateable) 8711359Sandreas@sandberg.pp.se : OutputStream(name, new stream_type_t()), 8811359Sandreas@sandberg.pp.se _mode(mode), _recreateable(recreateable), 8911359Sandreas@sandberg.pp.se _fstream(static_cast<stream_type_t *>(_stream)) 9011359Sandreas@sandberg.pp.se{ 9111359Sandreas@sandberg.pp.se _fstream->open(dir.resolve(_name).c_str(), _mode); 9211359Sandreas@sandberg.pp.se 9311359Sandreas@sandberg.pp.se assert(_fstream->is_open()); 9411359Sandreas@sandberg.pp.se} 9511359Sandreas@sandberg.pp.se 9611359Sandreas@sandberg.pp.setemplate<class StreamType> 9711359Sandreas@sandberg.pp.seOutputFile<StreamType>::~OutputFile() 9811359Sandreas@sandberg.pp.se{ 9911359Sandreas@sandberg.pp.se if (_fstream->is_open()) 10011359Sandreas@sandberg.pp.se _fstream->close(); 10111359Sandreas@sandberg.pp.se} 10211359Sandreas@sandberg.pp.se 10311359Sandreas@sandberg.pp.setemplate<class StreamType> 10411359Sandreas@sandberg.pp.sevoid 10511359Sandreas@sandberg.pp.seOutputFile<StreamType>::relocate(const OutputDirectory &dir) 10611359Sandreas@sandberg.pp.se{ 10711359Sandreas@sandberg.pp.se if (_recreateable) { 10811359Sandreas@sandberg.pp.se _fstream->close(); 10911359Sandreas@sandberg.pp.se _fstream->open(dir.resolve(_name).c_str(), _mode); 11011359Sandreas@sandberg.pp.se } 11111359Sandreas@sandberg.pp.se} 11211359Sandreas@sandberg.pp.se 11311359Sandreas@sandberg.pp.seOutputStream OutputDirectory::stdout("stdout", &cout); 11411359Sandreas@sandberg.pp.seOutputStream OutputDirectory::stderr("stderr", &cerr); 11511359Sandreas@sandberg.pp.se 1161388SN/A/** 1178634Schris.emmons@arm.com * @file This file manages creating / deleting output files for the simulator. 1181388SN/A */ 1191388SN/AOutputDirectory::OutputDirectory() 1201388SN/A{} 1211388SN/A 12211359Sandreas@sandberg.pp.seOutputDirectory::OutputDirectory(const std::string &name) 12311359Sandreas@sandberg.pp.se{ 12411359Sandreas@sandberg.pp.se setDirectory(name); 12511359Sandreas@sandberg.pp.se} 12611359Sandreas@sandberg.pp.se 1271388SN/AOutputDirectory::~OutputDirectory() 1285749Scws3k@cs.virginia.edu{ 12911359Sandreas@sandberg.pp.se for (auto& f: files) { 13011359Sandreas@sandberg.pp.se if (f.second) 13111359Sandreas@sandberg.pp.se delete f.second; 1325749Scws3k@cs.virginia.edu } 1335749Scws3k@cs.virginia.edu} 1345749Scws3k@cs.virginia.edu 13511359Sandreas@sandberg.pp.seOutputStream * 13611359Sandreas@sandberg.pp.seOutputDirectory::checkForStdio(const string &name) 1375749Scws3k@cs.virginia.edu{ 1385749Scws3k@cs.virginia.edu if (name == "cerr" || name == "stderr") 13911359Sandreas@sandberg.pp.se return &stderr; 1405749Scws3k@cs.virginia.edu 1415749Scws3k@cs.virginia.edu if (name == "cout" || name == "stdout") 14211359Sandreas@sandberg.pp.se return &stdout; 1435749Scws3k@cs.virginia.edu 1445749Scws3k@cs.virginia.edu return NULL; 1455749Scws3k@cs.virginia.edu} 1465749Scws3k@cs.virginia.edu 14711359Sandreas@sandberg.pp.sevoid 14811359Sandreas@sandberg.pp.seOutputDirectory::close(OutputStream *file) 1495749Scws3k@cs.virginia.edu{ 15011359Sandreas@sandberg.pp.se auto i = files.find(file->name()); 1518634Schris.emmons@arm.com if (i == files.end()) 1528634Schris.emmons@arm.com fatal("Attempted to close an unregistred file stream"); 1538634Schris.emmons@arm.com 1548634Schris.emmons@arm.com files.erase(i); 15511359Sandreas@sandberg.pp.se 15611359Sandreas@sandberg.pp.se delete file; 1578634Schris.emmons@arm.com} 1588634Schris.emmons@arm.com 1598634Schris.emmons@arm.comvoid 1601388SN/AOutputDirectory::setDirectory(const string &d) 1611388SN/A{ 16211359Sandreas@sandberg.pp.se const string old_dir(dir); 1631388SN/A 1641388SN/A dir = d; 1651388SN/A 1668634Schris.emmons@arm.com // guarantee that directory ends with a path separator 1678634Schris.emmons@arm.com if (dir[dir.size() - 1] != PATH_SEPARATOR) 1688634Schris.emmons@arm.com dir += PATH_SEPARATOR; 16911359Sandreas@sandberg.pp.se 17011359Sandreas@sandberg.pp.se // Try to create the directory. If it already exists, that's ok; 17111359Sandreas@sandberg.pp.se // otherwise, fail if we couldn't create it. 17211359Sandreas@sandberg.pp.se if ((mkdir(dir.c_str(), 0755) != 0) && (errno != EEXIST)) 17311359Sandreas@sandberg.pp.se fatal("Failed to create new output subdirectory '%s'\n", dir); 17411359Sandreas@sandberg.pp.se 17511359Sandreas@sandberg.pp.se // Check if we need to recreate anything 17611359Sandreas@sandberg.pp.se if (!old_dir.empty()) { 17711359Sandreas@sandberg.pp.se // Recreate output files 17811359Sandreas@sandberg.pp.se for (file_map_t::iterator i = files.begin(); i != files.end(); ++i) { 17911359Sandreas@sandberg.pp.se i->second->relocate(*this); 18011359Sandreas@sandberg.pp.se } 18111359Sandreas@sandberg.pp.se 18211359Sandreas@sandberg.pp.se // Relocate sub-directories 18311359Sandreas@sandberg.pp.se for (dir_map_t::iterator i = dirs.begin(); i != dirs.end(); ++i) { 18411359Sandreas@sandberg.pp.se i->second->setDirectory(dir + PATH_SEPARATOR + i->first); 18511359Sandreas@sandberg.pp.se } 18611359Sandreas@sandberg.pp.se } 18711359Sandreas@sandberg.pp.se 1881388SN/A} 1891388SN/A 1901388SN/Aconst string & 1915749Scws3k@cs.virginia.eduOutputDirectory::directory() const 1921388SN/A{ 1931388SN/A if (dir.empty()) 1941388SN/A panic("Output directory not set!"); 1951388SN/A 1961388SN/A return dir; 1971388SN/A} 1981388SN/A 1999398Sandreas.hansson@arm.comstring 2005749Scws3k@cs.virginia.eduOutputDirectory::resolve(const string &name) const 2011388SN/A{ 20211359Sandreas@sandberg.pp.se return !isAbsolute(name) ? dir + name : name; 2031388SN/A} 2041388SN/A 20511359Sandreas@sandberg.pp.seOutputStream * 20611259Ssascha.bischoff@ARM.comOutputDirectory::create(const string &name, bool binary, bool no_gz) 2071388SN/A{ 20811359Sandreas@sandberg.pp.se OutputStream *file = checkForStdio(name); 2095749Scws3k@cs.virginia.edu if (file) 2105749Scws3k@cs.virginia.edu return file; 2111388SN/A 21211359Sandreas@sandberg.pp.se const ios_base::openmode mode( 21311359Sandreas@sandberg.pp.se ios::trunc | (binary ? ios::binary : (ios::openmode)0)); 21411359Sandreas@sandberg.pp.se const bool recreateable(!isAbsolute(name)); 2151388SN/A 21611359Sandreas@sandberg.pp.se return open(name, mode, recreateable, no_gz); 2171388SN/A} 2181388SN/A 21911359Sandreas@sandberg.pp.seOutputStream * 22011359Sandreas@sandberg.pp.seOutputDirectory::open(const std::string &name, 22111359Sandreas@sandberg.pp.se ios_base::openmode mode, 22211359Sandreas@sandberg.pp.se bool recreateable, 22311359Sandreas@sandberg.pp.se bool no_gz) 22411359Sandreas@sandberg.pp.se{ 22511359Sandreas@sandberg.pp.se OutputStream *os; 22611359Sandreas@sandberg.pp.se 22711359Sandreas@sandberg.pp.se if (!no_gz && name.find(".gz", name.length() - 3) < name.length()) { 22811359Sandreas@sandberg.pp.se // Although we are creating an output stream, we still need to pass the 22911359Sandreas@sandberg.pp.se // correct mode for gzofstream as this used directly to set the file 23011359Sandreas@sandberg.pp.se // mode. 23111359Sandreas@sandberg.pp.se mode |= std::ios::out; 23211359Sandreas@sandberg.pp.se os = new OutputFile<gzofstream>(*this, name, mode, recreateable); 23311359Sandreas@sandberg.pp.se } else { 23411359Sandreas@sandberg.pp.se os = new OutputFile<ofstream>(*this, name, mode, recreateable); 23511359Sandreas@sandberg.pp.se } 23611359Sandreas@sandberg.pp.se 23711359Sandreas@sandberg.pp.se files[name] = os; 23811359Sandreas@sandberg.pp.se 23911359Sandreas@sandberg.pp.se return os; 24011359Sandreas@sandberg.pp.se} 24111359Sandreas@sandberg.pp.se 24211359Sandreas@sandberg.pp.seOutputStream * 2438634Schris.emmons@arm.comOutputDirectory::find(const string &name) const 2441388SN/A{ 24511359Sandreas@sandberg.pp.se OutputStream *file = checkForStdio(name); 2465749Scws3k@cs.virginia.edu if (file) 2475749Scws3k@cs.virginia.edu return file; 2481388SN/A 24911359Sandreas@sandberg.pp.se auto i = files.find(name); 2501388SN/A if (i != files.end()) 2511388SN/A return (*i).second; 2521388SN/A 2538634Schris.emmons@arm.com return NULL; 2541388SN/A} 2551388SN/A 25611359Sandreas@sandberg.pp.se 25711359Sandreas@sandberg.pp.seOutputStream * 25811359Sandreas@sandberg.pp.seOutputDirectory::findOrCreate(const std::string &name, bool binary) 2591388SN/A{ 26011359Sandreas@sandberg.pp.se OutputStream *os(find(name)); 26111359Sandreas@sandberg.pp.se if (os) 26211359Sandreas@sandberg.pp.se return os; 26311359Sandreas@sandberg.pp.se else 26411359Sandreas@sandberg.pp.se return create(name, binary); 2651388SN/A} 2668634Schris.emmons@arm.com 2678634Schris.emmons@arm.combool 2688634Schris.emmons@arm.comOutputDirectory::isFile(const string &name) const 2698634Schris.emmons@arm.com{ 2708634Schris.emmons@arm.com // definitely a file if in our data structure 2718634Schris.emmons@arm.com if (find(name) != NULL) return true; 2728634Schris.emmons@arm.com 2738634Schris.emmons@arm.com struct stat st_buf; 2748634Schris.emmons@arm.com int st = stat(name.c_str(), &st_buf); 2758634Schris.emmons@arm.com return (st == 0) && S_ISREG(st_buf.st_mode); 2768634Schris.emmons@arm.com} 2778634Schris.emmons@arm.com 27811359Sandreas@sandberg.pp.seOutputDirectory * 27911359Sandreas@sandberg.pp.seOutputDirectory::createSubdirectory(const string &name) 2808634Schris.emmons@arm.com{ 2818634Schris.emmons@arm.com const string new_dir = resolve(name); 2828634Schris.emmons@arm.com if (new_dir.find(directory()) == string::npos) 2838634Schris.emmons@arm.com fatal("Attempting to create subdirectory not in m5 output dir\n"); 2848634Schris.emmons@arm.com 28511359Sandreas@sandberg.pp.se OutputDirectory *dir(new OutputDirectory(new_dir)); 28611359Sandreas@sandberg.pp.se dirs[name] = dir; 2878634Schris.emmons@arm.com 28811359Sandreas@sandberg.pp.se return dir; 2898634Schris.emmons@arm.com} 2908634Schris.emmons@arm.com 2918634Schris.emmons@arm.comvoid 2928634Schris.emmons@arm.comOutputDirectory::remove(const string &name, bool recursive) 2938634Schris.emmons@arm.com{ 2948634Schris.emmons@arm.com const string fname = resolve(name); 2958634Schris.emmons@arm.com 2968634Schris.emmons@arm.com if (fname.find(directory()) == string::npos) 2978634Schris.emmons@arm.com fatal("Attempting to remove file/dir not in output dir\n"); 2988634Schris.emmons@arm.com 2998634Schris.emmons@arm.com if (isFile(fname)) { 3008634Schris.emmons@arm.com // close and release file if we have it open 30111359Sandreas@sandberg.pp.se auto i = files.find(fname); 30211359Sandreas@sandberg.pp.se if (i != files.end()) { 30311359Sandreas@sandberg.pp.se delete i->second; 30411359Sandreas@sandberg.pp.se files.erase(i); 3058634Schris.emmons@arm.com } 3068634Schris.emmons@arm.com 3078634Schris.emmons@arm.com if (::remove(fname.c_str()) != 0) 3088634Schris.emmons@arm.com fatal("Could not erase file '%s'\n", fname); 3098634Schris.emmons@arm.com } else { 3108634Schris.emmons@arm.com // assume 'name' is a directory 3118634Schris.emmons@arm.com if (recursive) { 3129550Sandreas.hansson@arm.com DIR *subdir = opendir(fname.c_str()); 3138634Schris.emmons@arm.com 3148634Schris.emmons@arm.com // silently ignore removal request for non-existent directory 3159550Sandreas.hansson@arm.com if ((!subdir) && (errno == ENOENT)) 3168634Schris.emmons@arm.com return; 3178634Schris.emmons@arm.com 3188634Schris.emmons@arm.com // fail on other errors 3199550Sandreas.hansson@arm.com if (!subdir) { 3208634Schris.emmons@arm.com perror("opendir"); 3218634Schris.emmons@arm.com fatal("Error opening directory for recursive removal '%s'\n", 32211359Sandreas@sandberg.pp.se fname); 3238634Schris.emmons@arm.com } 3248634Schris.emmons@arm.com 3259550Sandreas.hansson@arm.com struct dirent *de = readdir(subdir); 3268634Schris.emmons@arm.com while (de != NULL) { 3278634Schris.emmons@arm.com // ignore files starting with a '.'; user must delete those 3288634Schris.emmons@arm.com // manually if they really want to 3298634Schris.emmons@arm.com if (de->d_name[0] != '.') 3308634Schris.emmons@arm.com remove(name + PATH_SEPARATOR + de->d_name, recursive); 3318634Schris.emmons@arm.com 3329550Sandreas.hansson@arm.com de = readdir(subdir); 3338634Schris.emmons@arm.com } 33410412Sandreas.hansson@arm.com 33510412Sandreas.hansson@arm.com closedir(subdir); 3368634Schris.emmons@arm.com } 3378634Schris.emmons@arm.com 3388634Schris.emmons@arm.com // try to force recognition that we deleted the files in the directory 3398634Schris.emmons@arm.com sync(); 3408634Schris.emmons@arm.com 3418634Schris.emmons@arm.com if (::remove(fname.c_str()) != 0) { 3428634Schris.emmons@arm.com perror("Warning! 'remove' failed. Could not erase directory."); 3438634Schris.emmons@arm.com } 3448634Schris.emmons@arm.com } 3458634Schris.emmons@arm.com} 346