Home | History | Annotate | Download | only in util
      1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
      2 
      3 Licensed under the Apache License, Version 2.0 (the "License");
      4 you may not use this file except in compliance with the License.
      5 You may obtain a copy of the License at
      6 
      7     http://www.apache.org/licenses/LICENSE-2.0
      8 
      9 Unless required by applicable law or agreed to in writing, software
     10 distributed under the License is distributed on an "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 See the License for the specific language governing permissions and
     13 limitations under the License.
     14 ==============================================================================*/
     15 
     16 #include "tensorflow/core/util/events_writer.h"
     17 
     18 #include <stddef.h>  // for NULL
     19 
     20 #include "tensorflow/core/lib/core/errors.h"
     21 #include "tensorflow/core/lib/core/status.h"
     22 #include "tensorflow/core/lib/io/path.h"
     23 #include "tensorflow/core/lib/strings/strcat.h"
     24 #include "tensorflow/core/lib/strings/stringprintf.h"
     25 #include "tensorflow/core/platform/env.h"
     26 #include "tensorflow/core/platform/host_info.h"
     27 #include "tensorflow/core/platform/logging.h"
     28 #include "tensorflow/core/platform/types.h"
     29 #include "tensorflow/core/util/event.pb.h"
     30 
     31 namespace tensorflow {
     32 
     33 EventsWriter::EventsWriter(const string& file_prefix)
     34     // TODO(jeff,sanjay): Pass in env and use that here instead of Env::Default
     35     : env_(Env::Default()),
     36       file_prefix_(file_prefix),
     37       num_outstanding_events_(0) {}
     38 
     39 EventsWriter::~EventsWriter() {
     40   Close().IgnoreError();  // Autoclose in destructor.
     41 }
     42 
     43 Status EventsWriter::Init() { return InitWithSuffix(""); }
     44 
     45 Status EventsWriter::InitWithSuffix(const string& suffix) {
     46   file_suffix_ = suffix;
     47   return InitIfNeeded();
     48 }
     49 
     50 Status EventsWriter::InitIfNeeded() {
     51   if (recordio_writer_ != nullptr) {
     52     CHECK(!filename_.empty());
     53     if (!FileStillExists().ok()) {
     54       // Warn user of data loss and let .reset() below do basic cleanup.
     55       if (num_outstanding_events_ > 0) {
     56         LOG(WARNING) << "Re-initialization, attempting to open a new file, "
     57                      << num_outstanding_events_ << " events will be lost.";
     58       }
     59     } else {
     60       // No-op: File is present and writer is initialized.
     61       return Status::OK();
     62     }
     63   }
     64 
     65   int64 time_in_seconds = env_->NowMicros() / 1000000;
     66 
     67   filename_ =
     68       strings::Printf("%s.out.tfevents.%010lld.%s%s", file_prefix_.c_str(),
     69                       static_cast<int64>(time_in_seconds),
     70                       port::Hostname().c_str(), file_suffix_.c_str());
     71 
     72   // Reset recordio_writer (which has a reference to recordio_file_) so final
     73   // Flush() and Close() call have access to recordio_file_.
     74   recordio_writer_.reset();
     75 
     76   TF_RETURN_WITH_CONTEXT_IF_ERROR(
     77       env_->NewWritableFile(filename_, &recordio_file_),
     78       "Creating writable file ", filename_);
     79   recordio_writer_.reset(new io::RecordWriter(recordio_file_.get()));
     80   if (recordio_writer_ == nullptr) {
     81     return errors::Unknown("Could not create record writer");
     82   }
     83   num_outstanding_events_ = 0;
     84   VLOG(1) << "Successfully opened events file: " << filename_;
     85   {
     86     // Write the first event with the current version, and flush
     87     // right away so the file contents will be easily determined.
     88 
     89     Event event;
     90     event.set_wall_time(time_in_seconds);
     91     event.set_file_version(strings::StrCat(kVersionPrefix, kCurrentVersion));
     92     WriteEvent(event);
     93     TF_RETURN_WITH_CONTEXT_IF_ERROR(Flush(), "Flushing first event.");
     94   }
     95   return Status::OK();
     96 }
     97 
     98 string EventsWriter::FileName() {
     99   if (filename_.empty()) {
    100     InitIfNeeded().IgnoreError();
    101   }
    102   return filename_;
    103 }
    104 
    105 void EventsWriter::WriteSerializedEvent(StringPiece event_str) {
    106   if (recordio_writer_ == nullptr) {
    107     if (!InitIfNeeded().ok()) {
    108       LOG(ERROR) << "Write failed because file could not be opened.";
    109       return;
    110     }
    111   }
    112   num_outstanding_events_++;
    113   recordio_writer_->WriteRecord(event_str).IgnoreError();
    114 }
    115 
    116 // NOTE(touts); This is NOT the function called by the Python code.
    117 // Python calls WriteSerializedEvent(), see events_writer.i.
    118 void EventsWriter::WriteEvent(const Event& event) {
    119   string record;
    120   event.AppendToString(&record);
    121   WriteSerializedEvent(record);
    122 }
    123 
    124 Status EventsWriter::Flush() {
    125   if (num_outstanding_events_ == 0) return Status::OK();
    126   CHECK(recordio_file_ != nullptr) << "Unexpected NULL file";
    127 
    128   TF_RETURN_WITH_CONTEXT_IF_ERROR(recordio_writer_->Flush(), "Failed to flush ",
    129                                   num_outstanding_events_, " events to ",
    130                                   filename_);
    131   TF_RETURN_WITH_CONTEXT_IF_ERROR(recordio_file_->Sync(), "Failed to sync ",
    132                                   num_outstanding_events_, " events to ",
    133                                   filename_);
    134 
    135   // The FileStillExists() condition is necessary because
    136   // recordio_writer_->Sync() can return OK even if the underlying
    137   // file has been deleted.  EventWriter.FileDeletionBeforeWriting
    138   // demonstrates this and will fail if the FileHasDisappeared()
    139   // condition is removed.
    140   // Also, we deliberately attempt to Sync() before checking for a
    141   // disappearing file, in case for some file system File::Exists() is
    142   // false after File::Open() but before File::Sync().
    143   TF_RETURN_WITH_CONTEXT_IF_ERROR(FileStillExists(), "Failed to flush ",
    144                                   num_outstanding_events_, " events to ",
    145                                   filename_);
    146   VLOG(1) << "Wrote " << num_outstanding_events_ << " events to disk.";
    147   num_outstanding_events_ = 0;
    148   return Status::OK();
    149 }
    150 
    151 Status EventsWriter::Close() {
    152   Status status = Flush();
    153   if (recordio_file_ != nullptr) {
    154     Status close_status = recordio_file_->Close();
    155     if (!close_status.ok()) {
    156       status = close_status;
    157     }
    158     recordio_writer_.reset(nullptr);
    159     recordio_file_.reset(nullptr);
    160   }
    161   num_outstanding_events_ = 0;
    162   return status;
    163 }
    164 
    165 Status EventsWriter::FileStillExists() {
    166   if (env_->FileExists(filename_).ok()) {
    167     return Status::OK();
    168   }
    169   // This can happen even with non-null recordio_writer_ if some other
    170   // process has removed the file.
    171   return errors::Unknown("The events file ", filename_, " has disappeared.");
    172 }
    173 
    174 }  // namespace tensorflow
    175