output.cc revision 9550
12SN/A/*
213759Sgiacomo.gabrielli@arm.com * Copyright (c) 2005 The Regents of The University of Michigan
313759Sgiacomo.gabrielli@arm.com * All rights reserved.
413759Sgiacomo.gabrielli@arm.com *
513759Sgiacomo.gabrielli@arm.com * Redistribution and use in source and binary forms, with or without
613759Sgiacomo.gabrielli@arm.com * modification, are permitted provided that the following conditions are
713759Sgiacomo.gabrielli@arm.com * met: redistributions of source code must retain the above copyright
813759Sgiacomo.gabrielli@arm.com * notice, this list of conditions and the following disclaimer;
913759Sgiacomo.gabrielli@arm.com * redistributions in binary form must reproduce the above copyright
1013759Sgiacomo.gabrielli@arm.com * notice, this list of conditions and the following disclaimer in the
1113759Sgiacomo.gabrielli@arm.com * documentation and/or other materials provided with the distribution;
1213759Sgiacomo.gabrielli@arm.com * neither the name of the copyright holders nor the names of its
1313759Sgiacomo.gabrielli@arm.com * contributors may be used to endorse or promote products derived from
142188SN/A * this software without specific prior written permission.
152SN/A *
162SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
172SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
182SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
192SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
202SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
212SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
222SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
262SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272SN/A *
282SN/A * Authors: Nathan Binkert
292SN/A *          Chris Emmons
302SN/A */
312SN/A
322SN/A#include <sys/stat.h>
332SN/A#include <sys/types.h>
342SN/A#include <dirent.h>
352SN/A
362SN/A#include <cassert>
372SN/A#include <cerrno>
382SN/A#include <climits>
392665SN/A#include <cstdlib>
402665SN/A#include <fstream>
412665SN/A
422665SN/A#include <gzstream.hh>
432665SN/A
442SN/A#include "base/misc.hh"
452SN/A#include "base/output.hh"
4611793Sbrandon.potter@amd.com
4711793Sbrandon.potter@amd.comusing namespace std;
482SN/A
492SN/AOutputDirectory simout;
502465SN/A
513565Sgblack@eecs.umich.edu/**
525529Snate@binkert.org * @file This file manages creating / deleting output files for the simulator.
538777Sgblack@eecs.umich.edu */
541917SN/AOutputDirectory::OutputDirectory()
551070SN/A{}
561917SN/A
572188SN/AOutputDirectory::~OutputDirectory()
588777Sgblack@eecs.umich.edu{
598777Sgblack@eecs.umich.edu    for (map_t::iterator i = files.begin(); i != files.end(); i++) {
601917SN/A        if (i->second)
612290SN/A            delete i->second;
628777Sgblack@eecs.umich.edu    }
638706Sandreas.hansson@arm.com}
648799Sgblack@eecs.umich.edu
658809Sgblack@eecs.umich.edustd::ostream *
6610319SAndreas.Sandberg@ARM.comOutputDirectory::checkForStdio(const string &name) const
678793Sgblack@eecs.umich.edu{
688777Sgblack@eecs.umich.edu    if (name == "cerr" || name == "stderr")
691070SN/A        return &cerr;
701917SN/A
712519SN/A    if (name == "cout" || name == "stdout")
722SN/A        return &cout;
732SN/A
742SN/A    return NULL;
752SN/A}
768820Sgblack@eecs.umich.edu
7712406Sgabeblack@google.comostream *
7812406Sgabeblack@google.comOutputDirectory::openFile(const string &filename,
7910537Sandreas.hansson@arm.com                          ios_base::openmode mode)
8010537Sandreas.hansson@arm.com{
8113759Sgiacomo.gabrielli@arm.com    if (filename.find(".gz", filename.length()-3) < filename.length()) {
828766Sgblack@eecs.umich.edu        ogzstream *file = new ogzstream(filename.c_str(), mode);
838766Sgblack@eecs.umich.edu        if (!file->is_open())
848766Sgblack@eecs.umich.edu            fatal("Cannot open file %s", filename);
8511627Smichael.lebeane@amd.com        assert(files.find(filename) == files.end());
868766Sgblack@eecs.umich.edu        files[filename] = file;
879377Sgblack@eecs.umich.edu        return file;
882683Sktlim@umich.edu    } else {
8912406Sgabeblack@google.com        ofstream *file = new ofstream(filename.c_str(), mode);
909384SAndreas.Sandberg@arm.com        if (!file->is_open())
919384SAndreas.Sandberg@arm.com            fatal("Cannot open file %s", filename);
9213759Sgiacomo.gabrielli@arm.com        assert(files.find(filename) == files.end());
932SN/A        files[filename] = file;
942683Sktlim@umich.edu        return file;
952190SN/A    }
962680SN/A}
972290SN/A
986316Sgblack@eecs.umich.eduvoid
991917SN/AOutputDirectory::close(ostream *openStream) {
1008735Sandreas.hanson@arm.com    map_t::iterator i;
1011982SN/A    for (i = files.begin(); i != files.end(); i++) {
1021917SN/A        if (i->second != openStream)
1032683Sktlim@umich.edu            continue;
1042683Sktlim@umich.edu
1051917SN/A        ofstream *fs = dynamic_cast<ofstream*>(i->second);
1061917SN/A        if (fs) {
1071917SN/A            fs->close();
1081917SN/A            delete i->second;
1091917SN/A            break;
1101917SN/A        } else {
1111917SN/A            ogzstream *gfs = dynamic_cast<ogzstream*>(i->second);
1121917SN/A            if (gfs) {
1132521SN/A                gfs->close();
1145482Snate@binkert.org                delete i->second;
11512181Sgabeblack@google.com                break;
1162SN/A            }
1172862Sktlim@umich.edu        }
1182683Sktlim@umich.edu    }
1191070SN/A
1202680SN/A    if (i == files.end())
1211070SN/A        fatal("Attempted to close an unregistred file stream");
1221070SN/A
1231917SN/A    files.erase(i);
1242683Sktlim@umich.edu}
125180SN/A
1269441SAndreas.Sandberg@ARM.comvoid
1279478Snilay@cs.wisc.eduOutputDirectory::setDirectory(const string &d)
128180SN/A{
1299441SAndreas.Sandberg@ARM.com    if (!dir.empty())
1309441SAndreas.Sandberg@ARM.com        panic("Output directory already set!\n");
131180SN/A
1322864Sktlim@umich.edu    dir = d;
1332864Sktlim@umich.edu
1342864Sktlim@umich.edu    // guarantee that directory ends with a path separator
1352862Sktlim@umich.edu    if (dir[dir.size() - 1] != PATH_SEPARATOR)
1362862Sktlim@umich.edu        dir += PATH_SEPARATOR;
1372862Sktlim@umich.edu}
1382862Sktlim@umich.edu
1392862Sktlim@umich.educonst string &
1408793Sgblack@eecs.umich.eduOutputDirectory::directory() const
1418793Sgblack@eecs.umich.edu{
1425714Shsul@eecs.umich.edu    if (dir.empty())
1435715Shsul@eecs.umich.edu        panic("Output directory not set!");
1445714Shsul@eecs.umich.edu
1452862Sktlim@umich.edu    return dir;
1462862Sktlim@umich.edu}
1472862Sktlim@umich.edu
14810905Sandreas.sandberg@arm.comstring
149217SN/AOutputDirectory::resolve(const string &name) const
15010905Sandreas.sandberg@arm.com{
15110905Sandreas.sandberg@arm.com    return (name[0] != PATH_SEPARATOR) ? dir + name : name;
152217SN/A}
153217SN/A
154217SN/Aostream *
155217SN/AOutputDirectory::create(const string &name, bool binary)
15610905Sandreas.sandberg@arm.com{
157217SN/A    ostream *file = checkForStdio(name);
15810905Sandreas.sandberg@arm.com    if (file)
15910905Sandreas.sandberg@arm.com        return file;
160217SN/A
161217SN/A    string filename = resolve(name);
1622683Sktlim@umich.edu    ios_base::openmode mode =
1639461Snilay@cs.wisc.edu        ios::trunc | (binary ? ios::binary : (ios::openmode)0);
1649461Snilay@cs.wisc.edu    file = openFile(filename, mode);
1659461Snilay@cs.wisc.edu
1669461Snilay@cs.wisc.edu    return file;
1679461Snilay@cs.wisc.edu}
1689461Snilay@cs.wisc.edu
1692683Sktlim@umich.eduostream *
1702683Sktlim@umich.eduOutputDirectory::find(const string &name) const
17111359Sandreas@sandberg.pp.se{
17211359Sandreas@sandberg.pp.se    ostream *file = checkForStdio(name);
17311359Sandreas@sandberg.pp.se    if (file)
1742683Sktlim@umich.edu        return file;
175217SN/A
176217SN/A    const string filename = resolve(name);
17710407Smitch.hayenga@arm.com    map_t::const_iterator i = files.find(filename);
1782SN/A    if (i != files.end())
1792680SN/A        return (*i).second;
1802SN/A
1812SN/A    return NULL;
1827823Ssteve.reinhardt@amd.com}
1832680SN/A
18410407Smitch.hayenga@arm.combool
185393SN/AOutputDirectory::isFile(const std::ostream *os)
186393SN/A{
187393SN/A    return os && os != &cerr && os != &cout;
1882683Sktlim@umich.edu}
189393SN/A
1902680SN/Abool
191393SN/AOutputDirectory::isFile(const string &name) const
192393SN/A{
1937823Ssteve.reinhardt@amd.com    // definitely a file if in our data structure
1947823Ssteve.reinhardt@amd.com    if (find(name) != NULL) return true;
1952680SN/A
1968735Sandreas.hanson@arm.com    struct stat st_buf;
1972SN/A    int st = stat(name.c_str(), &st_buf);
1982SN/A    return (st == 0) && S_ISREG(st_buf.st_mode);
199393SN/A}
200393SN/A
2012683Sktlim@umich.edustring
202393SN/AOutputDirectory::createSubdirectory(const string &name) const
2032680SN/A{
204393SN/A    const string new_dir = resolve(name);
205393SN/A    if (new_dir.find(directory()) == string::npos)
2062680SN/A        fatal("Attempting to create subdirectory not in m5 output dir\n");
2078735Sandreas.hanson@arm.com
208393SN/A    // if it already exists, that's ok; otherwise, fail if we couldn't create
209393SN/A    if ((mkdir(new_dir.c_str(), 0755) != 0) && (errno != EEXIST))
210393SN/A        fatal("Failed to create new output subdirectory '%s'\n", new_dir);
211393SN/A
2122683Sktlim@umich.edu    return name + PATH_SEPARATOR;
2132SN/A}
2148793Sgblack@eecs.umich.edu
2152341SN/Avoid
2162SN/AOutputDirectory::remove(const string &name, bool recursive)
217716SN/A{
218716SN/A    const string fname = resolve(name);
2192683Sktlim@umich.edu
2202190SN/A    if (fname.find(directory()) == string::npos)
2212680SN/A        fatal("Attempting to remove file/dir not in output dir\n");
2222190SN/A
2232190SN/A    if (isFile(fname)) {
22410319SAndreas.Sandberg@ARM.com        // close and release file if we have it open
22510319SAndreas.Sandberg@ARM.com        map_t::iterator itr = files.find(fname);
22610319SAndreas.Sandberg@ARM.com        if (itr != files.end()) {
22710319SAndreas.Sandberg@ARM.com            delete itr->second;
22810319SAndreas.Sandberg@ARM.com            files.erase(itr);
22910319SAndreas.Sandberg@ARM.com        }
23010319SAndreas.Sandberg@ARM.com
23110319SAndreas.Sandberg@ARM.com        if (::remove(fname.c_str()) != 0)
23210319SAndreas.Sandberg@ARM.com            fatal("Could not erase file '%s'\n", fname);
23310319SAndreas.Sandberg@ARM.com    } else {
23410319SAndreas.Sandberg@ARM.com        // assume 'name' is a directory
23510319SAndreas.Sandberg@ARM.com        if (recursive) {
23610319SAndreas.Sandberg@ARM.com            DIR *subdir = opendir(fname.c_str());
23710319SAndreas.Sandberg@ARM.com
23810319SAndreas.Sandberg@ARM.com            // silently ignore removal request for non-existent directory
239            if ((!subdir) && (errno == ENOENT))
240                return;
241
242            // fail on other errors
243            if (!subdir) {
244                perror("opendir");
245                fatal("Error opening directory for recursive removal '%s'\n",
246                    fname);
247            }
248
249            struct dirent *de = readdir(subdir);
250            while (de != NULL) {
251                // ignore files starting with a '.'; user must delete those
252                //   manually if they really want to
253                if (de->d_name[0] != '.')
254                    remove(name + PATH_SEPARATOR + de->d_name, recursive);
255
256                de = readdir(subdir);
257            }
258        }
259
260        // try to force recognition that we deleted the files in the directory
261        sync();
262
263        if (::remove(fname.c_str()) != 0) {
264            perror("Warning!  'remove' failed.  Could not erase directory.");
265        }
266    }
267}
268