output.cc revision 11259
11388SN/A/*
21388SN/A * Copyright (c) 2005 The Regents of The University of Michigan
31388SN/A * All rights reserved.
41388SN/A *
51388SN/A * Redistribution and use in source and binary forms, with or without
61388SN/A * modification, are permitted provided that the following conditions are
71388SN/A * met: redistributions of source code must retain the above copyright
81388SN/A * notice, this list of conditions and the following disclaimer;
91388SN/A * redistributions in binary form must reproduce the above copyright
101388SN/A * notice, this list of conditions and the following disclaimer in the
111388SN/A * documentation and/or other materials provided with the distribution;
121388SN/A * neither the name of the copyright holders nor the names of its
131388SN/A * contributors may be used to endorse or promote products derived from
141388SN/A * this software without specific prior written permission.
151388SN/A *
161388SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
171388SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
181388SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
191388SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
201388SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
211388SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
221388SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
231388SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
241388SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
251388SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
261388SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272665Ssaidi@eecs.umich.edu *
282665Ssaidi@eecs.umich.edu * Authors: Nathan Binkert
298634Schris.emmons@arm.com *          Chris Emmons
301388SN/A */
311388SN/A
321388SN/A#include <sys/stat.h>
331388SN/A#include <sys/types.h>
348634Schris.emmons@arm.com#include <dirent.h>
3510810Sbr@bsdpad.com#include <unistd.h>
361388SN/A
378634Schris.emmons@arm.com#include <cassert>
388229Snate@binkert.org#include <cerrno>
398229Snate@binkert.org#include <climits>
408229Snate@binkert.org#include <cstdlib>
411388SN/A#include <fstream>
421388SN/A
435749Scws3k@cs.virginia.edu#include <gzstream.hh>
445749Scws3k@cs.virginia.edu
451388SN/A#include "base/misc.hh"
461388SN/A#include "base/output.hh"
471388SN/A
481388SN/Ausing namespace std;
491388SN/A
501388SN/AOutputDirectory simout;
511388SN/A
521388SN/A/**
538634Schris.emmons@arm.com * @file This file manages creating / deleting output files for the simulator.
541388SN/A */
551388SN/AOutputDirectory::OutputDirectory()
561388SN/A{}
571388SN/A
581388SN/AOutputDirectory::~OutputDirectory()
595749Scws3k@cs.virginia.edu{
605749Scws3k@cs.virginia.edu    for (map_t::iterator i = files.begin(); i != files.end(); i++) {
615749Scws3k@cs.virginia.edu        if (i->second)
625749Scws3k@cs.virginia.edu            delete i->second;
635749Scws3k@cs.virginia.edu    }
645749Scws3k@cs.virginia.edu}
655749Scws3k@cs.virginia.edu
665749Scws3k@cs.virginia.edustd::ostream *
675749Scws3k@cs.virginia.eduOutputDirectory::checkForStdio(const string &name) const
685749Scws3k@cs.virginia.edu{
695749Scws3k@cs.virginia.edu    if (name == "cerr" || name == "stderr")
705749Scws3k@cs.virginia.edu        return &cerr;
715749Scws3k@cs.virginia.edu
725749Scws3k@cs.virginia.edu    if (name == "cout" || name == "stdout")
735749Scws3k@cs.virginia.edu        return &cout;
745749Scws3k@cs.virginia.edu
755749Scws3k@cs.virginia.edu    return NULL;
765749Scws3k@cs.virginia.edu}
775749Scws3k@cs.virginia.edu
785749Scws3k@cs.virginia.eduostream *
795749Scws3k@cs.virginia.eduOutputDirectory::openFile(const string &filename,
8011259Ssascha.bischoff@ARM.com                          ios_base::openmode mode, bool no_gz)
815749Scws3k@cs.virginia.edu{
8211259Ssascha.bischoff@ARM.com    bool gz = !no_gz;
8311259Ssascha.bischoff@ARM.com    gz = gz && filename.find(".gz", filename.length()-3) < filename.length();
8411259Ssascha.bischoff@ARM.com    if (gz) {
855749Scws3k@cs.virginia.edu        ogzstream *file = new ogzstream(filename.c_str(), mode);
865749Scws3k@cs.virginia.edu        if (!file->is_open())
875749Scws3k@cs.virginia.edu            fatal("Cannot open file %s", filename);
888634Schris.emmons@arm.com        assert(files.find(filename) == files.end());
898634Schris.emmons@arm.com        files[filename] = file;
905749Scws3k@cs.virginia.edu        return file;
915749Scws3k@cs.virginia.edu    } else {
925749Scws3k@cs.virginia.edu        ofstream *file = new ofstream(filename.c_str(), mode);
935749Scws3k@cs.virginia.edu        if (!file->is_open())
945749Scws3k@cs.virginia.edu            fatal("Cannot open file %s", filename);
958634Schris.emmons@arm.com        assert(files.find(filename) == files.end());
968634Schris.emmons@arm.com        files[filename] = file;
975749Scws3k@cs.virginia.edu        return file;
985749Scws3k@cs.virginia.edu    }
995749Scws3k@cs.virginia.edu}
1001388SN/A
1011388SN/Avoid
1028634Schris.emmons@arm.comOutputDirectory::close(ostream *openStream) {
1038634Schris.emmons@arm.com    map_t::iterator i;
1048634Schris.emmons@arm.com    for (i = files.begin(); i != files.end(); i++) {
1058634Schris.emmons@arm.com        if (i->second != openStream)
1068634Schris.emmons@arm.com            continue;
1078634Schris.emmons@arm.com
1088634Schris.emmons@arm.com        ofstream *fs = dynamic_cast<ofstream*>(i->second);
1098634Schris.emmons@arm.com        if (fs) {
1108634Schris.emmons@arm.com            fs->close();
1118634Schris.emmons@arm.com            delete i->second;
1128634Schris.emmons@arm.com            break;
1138634Schris.emmons@arm.com        } else {
1148634Schris.emmons@arm.com            ogzstream *gfs = dynamic_cast<ogzstream*>(i->second);
1158634Schris.emmons@arm.com            if (gfs) {
1168634Schris.emmons@arm.com                gfs->close();
1178634Schris.emmons@arm.com                delete i->second;
1188634Schris.emmons@arm.com                break;
1198634Schris.emmons@arm.com            }
1208634Schris.emmons@arm.com        }
1218634Schris.emmons@arm.com    }
1228634Schris.emmons@arm.com
1238634Schris.emmons@arm.com    if (i == files.end())
1248634Schris.emmons@arm.com        fatal("Attempted to close an unregistred file stream");
1258634Schris.emmons@arm.com
1268634Schris.emmons@arm.com    files.erase(i);
1278634Schris.emmons@arm.com}
1288634Schris.emmons@arm.com
1298634Schris.emmons@arm.comvoid
1301388SN/AOutputDirectory::setDirectory(const string &d)
1311388SN/A{
1321388SN/A    if (!dir.empty())
1331388SN/A        panic("Output directory already set!\n");
1341388SN/A
1351388SN/A    dir = d;
1361388SN/A
1378634Schris.emmons@arm.com    // guarantee that directory ends with a path separator
1388634Schris.emmons@arm.com    if (dir[dir.size() - 1] != PATH_SEPARATOR)
1398634Schris.emmons@arm.com        dir += PATH_SEPARATOR;
1401388SN/A}
1411388SN/A
1421388SN/Aconst string &
1435749Scws3k@cs.virginia.eduOutputDirectory::directory() const
1441388SN/A{
1451388SN/A    if (dir.empty())
1461388SN/A        panic("Output directory not set!");
1471388SN/A
1481388SN/A    return dir;
1491388SN/A}
1501388SN/A
1519398Sandreas.hansson@arm.comstring
1525749Scws3k@cs.virginia.eduOutputDirectory::resolve(const string &name) const
1531388SN/A{
1548634Schris.emmons@arm.com    return (name[0] != PATH_SEPARATOR) ? dir + name : name;
1551388SN/A}
1561388SN/A
1571388SN/Aostream *
15811259Ssascha.bischoff@ARM.comOutputDirectory::create(const string &name, bool binary, bool no_gz)
1591388SN/A{
1605749Scws3k@cs.virginia.edu    ostream *file = checkForStdio(name);
1615749Scws3k@cs.virginia.edu    if (file)
1625749Scws3k@cs.virginia.edu        return file;
1631388SN/A
1645749Scws3k@cs.virginia.edu    string filename = resolve(name);
1655749Scws3k@cs.virginia.edu    ios_base::openmode mode =
1668989SAli.Saidi@ARM.com        ios::trunc | (binary ? ios::binary : (ios::openmode)0);
16711259Ssascha.bischoff@ARM.com    file = openFile(filename, mode, no_gz);
1681388SN/A
1691388SN/A    return file;
1701388SN/A}
1711388SN/A
1721388SN/Aostream *
1738634Schris.emmons@arm.comOutputDirectory::find(const string &name) const
1741388SN/A{
1755749Scws3k@cs.virginia.edu    ostream *file = checkForStdio(name);
1765749Scws3k@cs.virginia.edu    if (file)
1775749Scws3k@cs.virginia.edu        return file;
1781388SN/A
1798634Schris.emmons@arm.com    const string filename = resolve(name);
1808634Schris.emmons@arm.com    map_t::const_iterator i = files.find(filename);
1811388SN/A    if (i != files.end())
1821388SN/A        return (*i).second;
1831388SN/A
1848634Schris.emmons@arm.com    return NULL;
1851388SN/A}
1861388SN/A
1871388SN/Abool
1881388SN/AOutputDirectory::isFile(const std::ostream *os)
1891388SN/A{
1901388SN/A    return os && os != &cerr && os != &cout;
1911388SN/A}
1928634Schris.emmons@arm.com
1938634Schris.emmons@arm.combool
1948634Schris.emmons@arm.comOutputDirectory::isFile(const string &name) const
1958634Schris.emmons@arm.com{
1968634Schris.emmons@arm.com    // definitely a file if in our data structure
1978634Schris.emmons@arm.com    if (find(name) != NULL) return true;
1988634Schris.emmons@arm.com
1998634Schris.emmons@arm.com    struct stat st_buf;
2008634Schris.emmons@arm.com    int st = stat(name.c_str(), &st_buf);
2018634Schris.emmons@arm.com    return (st == 0) && S_ISREG(st_buf.st_mode);
2028634Schris.emmons@arm.com}
2038634Schris.emmons@arm.com
2048634Schris.emmons@arm.comstring
2058634Schris.emmons@arm.comOutputDirectory::createSubdirectory(const string &name) const
2068634Schris.emmons@arm.com{
2078634Schris.emmons@arm.com    const string new_dir = resolve(name);
2088634Schris.emmons@arm.com    if (new_dir.find(directory()) == string::npos)
2098634Schris.emmons@arm.com        fatal("Attempting to create subdirectory not in m5 output dir\n");
2108634Schris.emmons@arm.com
2118634Schris.emmons@arm.com    // if it already exists, that's ok; otherwise, fail if we couldn't create
2128634Schris.emmons@arm.com    if ((mkdir(new_dir.c_str(), 0755) != 0) && (errno != EEXIST))
2138634Schris.emmons@arm.com        fatal("Failed to create new output subdirectory '%s'\n", new_dir);
2148634Schris.emmons@arm.com
2158634Schris.emmons@arm.com    return name + PATH_SEPARATOR;
2168634Schris.emmons@arm.com}
2178634Schris.emmons@arm.com
2188634Schris.emmons@arm.comvoid
2198634Schris.emmons@arm.comOutputDirectory::remove(const string &name, bool recursive)
2208634Schris.emmons@arm.com{
2218634Schris.emmons@arm.com    const string fname = resolve(name);
2228634Schris.emmons@arm.com
2238634Schris.emmons@arm.com    if (fname.find(directory()) == string::npos)
2248634Schris.emmons@arm.com        fatal("Attempting to remove file/dir not in output dir\n");
2258634Schris.emmons@arm.com
2268634Schris.emmons@arm.com    if (isFile(fname)) {
2278634Schris.emmons@arm.com        // close and release file if we have it open
2288634Schris.emmons@arm.com        map_t::iterator itr = files.find(fname);
2298634Schris.emmons@arm.com        if (itr != files.end()) {
2308634Schris.emmons@arm.com            delete itr->second;
2318634Schris.emmons@arm.com            files.erase(itr);
2328634Schris.emmons@arm.com        }
2338634Schris.emmons@arm.com
2348634Schris.emmons@arm.com        if (::remove(fname.c_str()) != 0)
2358634Schris.emmons@arm.com            fatal("Could not erase file '%s'\n", fname);
2368634Schris.emmons@arm.com    } else {
2378634Schris.emmons@arm.com        // assume 'name' is a directory
2388634Schris.emmons@arm.com        if (recursive) {
2399550Sandreas.hansson@arm.com            DIR *subdir = opendir(fname.c_str());
2408634Schris.emmons@arm.com
2418634Schris.emmons@arm.com            // silently ignore removal request for non-existent directory
2429550Sandreas.hansson@arm.com            if ((!subdir) && (errno == ENOENT))
2438634Schris.emmons@arm.com                return;
2448634Schris.emmons@arm.com
2458634Schris.emmons@arm.com            // fail on other errors
2469550Sandreas.hansson@arm.com            if (!subdir) {
2478634Schris.emmons@arm.com                perror("opendir");
2488634Schris.emmons@arm.com                fatal("Error opening directory for recursive removal '%s'\n",
2498634Schris.emmons@arm.com                    fname);
2508634Schris.emmons@arm.com            }
2518634Schris.emmons@arm.com
2529550Sandreas.hansson@arm.com            struct dirent *de = readdir(subdir);
2538634Schris.emmons@arm.com            while (de != NULL) {
2548634Schris.emmons@arm.com                // ignore files starting with a '.'; user must delete those
2558634Schris.emmons@arm.com                //   manually if they really want to
2568634Schris.emmons@arm.com                if (de->d_name[0] != '.')
2578634Schris.emmons@arm.com                    remove(name + PATH_SEPARATOR + de->d_name, recursive);
2588634Schris.emmons@arm.com
2599550Sandreas.hansson@arm.com                de = readdir(subdir);
2608634Schris.emmons@arm.com            }
26110412Sandreas.hansson@arm.com
26210412Sandreas.hansson@arm.com            closedir(subdir);
2638634Schris.emmons@arm.com        }
2648634Schris.emmons@arm.com
2658634Schris.emmons@arm.com        // try to force recognition that we deleted the files in the directory
2668634Schris.emmons@arm.com        sync();
2678634Schris.emmons@arm.com
2688634Schris.emmons@arm.com        if (::remove(fname.c_str()) != 0) {
2698634Schris.emmons@arm.com            perror("Warning!  'remove' failed.  Could not erase directory.");
2708634Schris.emmons@arm.com        }
2718634Schris.emmons@arm.com    }
2728634Schris.emmons@arm.com}
273