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