fd_array.cc revision 12697:cd71b966be1e
1/*
2 * Copyright (c) 2016 Advanced Micro Devices, Inc.
3 * All rights reserved.
4 *
5 * For use for simulation and test purposes only
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 * Authors: Brandon Potter
34 */
35
36#include "sim/fd_array.hh"
37
38#include <fcntl.h>
39#include <unistd.h>
40
41#include <array>
42#include <memory>
43#include <string>
44
45#include "base/logging.hh"
46#include "params/Process.hh"
47#include "sim/fd_entry.hh"
48
49FDArray::FDArray(std::string const& input, std::string const& output,
50                 std::string const& errout)
51    : _input(input), _output(output), _errout(errout), _fdArray(),
52      imap {{"",       -1},
53            {"cin",    STDIN_FILENO},
54            {"stdin",  STDIN_FILENO}},
55      oemap{{"",       -1},
56            {"cout",   STDOUT_FILENO},
57            {"stdout", STDOUT_FILENO},
58            {"cerr",   STDERR_FILENO},
59            {"stderr", STDERR_FILENO}}
60{
61    int sim_fd;
62    std::map<std::string, int>::iterator it;
63
64    /**
65     * Search through the input options and setup the default fd if match is
66     * found; otherwise, open an input file and seek to location.
67     */
68    if ((it = imap.find(input)) != imap.end())
69        sim_fd = it->second;
70    else
71        sim_fd = openInputFile(input);
72
73    auto ffd = std::make_shared<FileFDEntry>(sim_fd, O_RDONLY, input, false);
74    _fdArray[STDIN_FILENO] = ffd;
75
76    /**
77     * Search through the output/error options and setup the default fd if
78     * match is found; otherwise, open an output file and seek to location.
79     */
80    if ((it = oemap.find(output)) != oemap.end())
81        sim_fd = it->second;
82    else
83        sim_fd = openOutputFile(output);
84
85    ffd = std::make_shared<FileFDEntry>(sim_fd, O_WRONLY | O_CREAT | O_TRUNC,
86                                        output, false);
87    _fdArray[STDOUT_FILENO] = ffd;
88
89    if (output == errout)
90        ; /* Reuse the same file descriptor if these match. */
91    else if ((it = oemap.find(errout)) != oemap.end())
92        sim_fd = it->second;
93    else
94        sim_fd = openOutputFile(errout);
95
96    ffd = std::make_shared<FileFDEntry>(sim_fd, O_WRONLY | O_CREAT | O_TRUNC,
97                                        errout, false);
98    _fdArray[STDERR_FILENO] = ffd;
99}
100
101void
102FDArray::updateFileOffsets()
103{
104    for (auto& fdp : _fdArray) {
105        /**
106         * It only makes sense to check the offsets if the file descriptor
107         * type is 'File' (which indicates that this file is backed by a
108         * file on the host). If the type is File, then record the offset.
109         */
110        auto ffd = std::dynamic_pointer_cast<FileFDEntry>(fdp);
111
112        if (!ffd)
113            continue;
114
115        /**
116         * Use lseek with SEEK_CUR with offset 0 to figure out where the
117         * offset currently resides and pass that back to our setter.
118         */
119        int sim_fd = ffd->getSimFD();
120        ffd->setFileOffset(lseek(sim_fd, 0, SEEK_CUR));
121    }
122}
123
124void
125FDArray::restoreFileOffsets()
126{
127    /**
128     * Use this lambda to highlight what we mean to do with the seek.
129     * Notice that this either seeks correctly (sets the file location on the
130     * host) or it fails with a fatal. The error is fatal because it's not
131     * possible to guarantee that the simulation will proceed as it should
132     * have in the same way that it would have proceeded sans checkpoints.
133     */
134    auto seek = [] (std::shared_ptr<FileFDEntry> ffd)
135    {
136        if (lseek(ffd->getSimFD(), ffd->getFileOffset(), SEEK_SET) < 0)
137            fatal("Unable to seek to location in %s", ffd->getFileName());
138    };
139
140    std::map<std::string, int>::iterator it;
141
142    /**
143     * Search through the input options and set fd if match is found;
144     * otherwise, open an input file and seek to location.
145     * Check if user has specified a different input file, and if so, use it
146     * instead of the file specified in the checkpoint. This also resets the
147     * file offset from the checkpointed value
148     */
149    std::shared_ptr<FDEntry> stdin_fde = _fdArray[STDIN_FILENO];
150    auto stdin_ffd = std::dynamic_pointer_cast<FileFDEntry>(stdin_fde);
151
152    if (_input != stdin_ffd->getFileName()) {
153        warn("Using new input file (%s) rather than checkpointed (%s)\n",
154             _input, stdin_ffd->getFileName());
155        stdin_ffd->setFileName(_input);
156        stdin_ffd->setFileOffset(0);
157    }
158
159    if ((it = imap.find(stdin_ffd->getFileName())) != imap.end()) {
160        stdin_ffd->setSimFD(it->second);
161    } else {
162        stdin_ffd->setSimFD(openInputFile(stdin_ffd->getFileName()));
163        seek(stdin_ffd);
164    }
165
166    /**
167     * Search through the output options and set fd if match is found;
168     * otherwise, open an output file and seek to location.
169     * Check if user has specified a different output file, and if so, use it
170     * instead of the file specified in the checkpoint. This also resets the
171     * file offset from the checkpointed value
172     */
173    std::shared_ptr<FDEntry> stdout_fde = _fdArray[STDOUT_FILENO];
174    auto stdout_ffd = std::dynamic_pointer_cast<FileFDEntry>(stdout_fde);
175
176    if (_output != stdout_ffd->getFileName()) {
177        warn("Using new output file (%s) rather than checkpointed (%s)\n",
178             _output, stdout_ffd->getFileName());
179        stdout_ffd->setFileName(_output);
180        stdout_ffd->setFileOffset(0);
181    }
182
183    if ((it = oemap.find(stdout_ffd->getFileName())) != oemap.end()) {
184        stdout_ffd->setSimFD(it->second);
185    } else {
186        stdout_ffd->setSimFD(openOutputFile(stdout_ffd->getFileName()));
187        seek(stdout_ffd);
188    }
189
190    /**
191     * Search through the error options and set fd if match is found;
192     * otherwise, open an error file and seek to location.
193     * Check if user has specified a different error file, and if so, use it
194     * instead of the file specified in the checkpoint. This also resets the
195     * file offset from the checkpointed value
196     */
197    std::shared_ptr<FDEntry> stderr_fde = _fdArray[STDERR_FILENO];
198    auto stderr_ffd = std::dynamic_pointer_cast<FileFDEntry>(stderr_fde);
199
200    if (_errout != stderr_ffd->getFileName()) {
201        warn("Using new error file (%s) rather than checkpointed (%s)\n",
202             _errout, stderr_ffd->getFileName());
203        stderr_ffd->setFileName(_errout);
204        stderr_ffd->setFileOffset(0);
205    }
206
207    if (stdout_ffd->getFileName() == stderr_ffd->getFileName()) {
208        /* Reuse the same sim_fd file descriptor if these match. */
209        stderr_ffd->setSimFD(stdout_ffd->getSimFD());
210    } else if ((it = oemap.find(stderr_ffd->getFileName())) != oemap.end()) {
211        stderr_ffd->setSimFD(it->second);
212    } else {
213        stderr_ffd->setSimFD(openOutputFile(stderr_ffd->getFileName()));
214        seek(stderr_ffd);
215    }
216
217    for (int tgt_fd = 3; tgt_fd < _fdArray.size(); tgt_fd++) {
218        std::shared_ptr<FDEntry> fdp = _fdArray[tgt_fd];
219        if (!fdp)
220            continue;
221
222        /* Need to reconnect pipe ends. */
223        if (auto pfd = std::dynamic_pointer_cast<PipeFDEntry>(fdp)) {
224            /**
225             * Check which end of the pipe we are looking at; we only want
226             * to setup the pipe once so we arbitrarily choose the read
227             * end to be the end that sets up the pipe.
228             */
229            if (pfd->getEndType() == PipeFDEntry::EndType::write)
230                continue;
231
232            /* Setup the pipe or fatal out of the simulation. */
233            int fd_pair[2];
234            if (pipe(fd_pair) < 0)
235                fatal("Unable to create new pipe");
236
237            /**
238             * Reconstruct the ends of the pipe by reassigning the pipe
239             * that we created on the host. This one is the read end.
240             */
241            pfd->setSimFD(fd_pair[0]);
242
243            /**
244             * Grab the write end by referencing the read ends source and
245             * using that tgt_fd to index the array.
246             */
247            int prs = pfd->getPipeReadSource();
248            std::shared_ptr<FDEntry> write_fdp = _fdArray[prs];
249
250            /* Now cast it and make sure that we are still sane. */
251            auto write_pfd = std::dynamic_pointer_cast<PipeFDEntry>(write_fdp);
252
253            /* Hook up the write end back to the right side of the pipe. */
254            write_pfd->setSimFD(fd_pair[1]);
255        }
256
257        /* Need to reassign 'driver'. */
258        if (auto dfd = std::dynamic_pointer_cast<DeviceFDEntry>(fdp)) {
259            /**
260             * Yeah, how does one retain the entire driver state from this
261             * particular set of code? If you figure it out, add some code
262             * here to rectify the issue.
263             */
264            fatal("Unable to restore checkpoints with emulated drivers");
265        }
266
267        /* Need to open files and seek. */
268        if (auto ffd = std::dynamic_pointer_cast<FileFDEntry>(fdp)) {
269            /**
270             * Assume that this has the mode of an output file so there's no
271             * need to worry about properly recording the mode. If you're
272             * reading this and this happens to be your issue, at least be
273             * happy that you've discovered the issue (and not mad at me).
274             * Onward ho!
275             */
276            int sim_fd = openFile(ffd->getFileName(), ffd->getFlags(), 0664);
277            ffd->setSimFD(sim_fd);
278            seek(ffd);
279        }
280    }
281}
282
283int
284FDArray::allocFD(std::shared_ptr<FDEntry> in)
285{
286    for (int i = 0; i < _fdArray.size(); i++) {
287        std::shared_ptr<FDEntry> fdp = _fdArray[i];
288        if (!fdp) {
289            _fdArray[i] = in;
290            return i;
291        }
292    }
293    fatal("Out of target file descriptors");
294}
295
296int
297FDArray::openFile(std::string const& filename, int flags, mode_t mode) const
298{
299    int sim_fd = open(filename.c_str(), flags, mode);
300    if (sim_fd != -1)
301        return sim_fd;
302    fatal("Unable to open %s with mode %O", filename, mode);
303}
304
305int
306FDArray::openInputFile(std::string const& filename) const
307{
308    return openFile(filename, O_RDONLY, 00);
309}
310
311int
312FDArray::openOutputFile(std::string const& filename) const
313{
314    return openFile(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
315}
316
317std::shared_ptr<FDEntry>
318FDArray::getFDEntry(int tgt_fd)
319{
320    assert(0 <= tgt_fd && tgt_fd < _fdArray.size());
321    return _fdArray[tgt_fd];
322}
323
324void
325FDArray::setFDEntry(int tgt_fd, std::shared_ptr<FDEntry> fdep)
326{
327    assert(0 <= tgt_fd && tgt_fd < _fdArray.size());
328    _fdArray[tgt_fd] = fdep;
329}
330
331int
332FDArray::closeFDEntry(int tgt_fd)
333{
334    if (tgt_fd >= _fdArray.size() || tgt_fd < 0)
335        return -EBADF;
336
337    int sim_fd = -1;
338    auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>(_fdArray[tgt_fd]);
339    if (hbfdp)
340        sim_fd = hbfdp->getSimFD();
341
342    int status = 0;
343    if (sim_fd > 2)
344        status = close(sim_fd);
345
346    if (status == 0)
347        _fdArray[tgt_fd] = nullptr;
348
349    return status;
350}
351