fd_array.cc revision 13029:75abda747dc3
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 : _fdArray(), _input(input), _output(output), _errout(errout), 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