Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
node_trace_writer.cc
Go to the documentation of this file.
2 
3 #include <string.h>
4 #include <fcntl.h>
5 
6 #include "util.h"
7 
8 namespace node {
9 namespace tracing {
10 
11 NodeTraceWriter::NodeTraceWriter(uv_loop_t* tracing_loop)
12  : tracing_loop_(tracing_loop) {
13  flush_signal_.data = this;
14  int err = uv_async_init(tracing_loop_, &flush_signal_, FlushSignalCb);
15  CHECK_EQ(err, 0);
16 
17  exit_signal_.data = this;
18  err = uv_async_init(tracing_loop_, &exit_signal_, ExitSignalCb);
19  CHECK_EQ(err, 0);
20 }
21 
22 void NodeTraceWriter::WriteSuffix() {
23  // If our final log file has traces, then end the file appropriately.
24  // This means that if no trace events are recorded, then no trace file is
25  // produced.
26  bool should_flush = false;
27  {
28  Mutex::ScopedLock scoped_lock(stream_mutex_);
29  if (total_traces_ > 0) {
30  total_traces_ = 0; // so we don't write it again in FlushPrivate
31  // Appends "]}" to stream_.
32  delete json_trace_writer_;
33  should_flush = true;
34  }
35  }
36  if (should_flush) {
37  Flush(true);
38  }
39 }
40 
42  WriteSuffix();
43  uv_fs_t req;
44  int err;
45  if (fd_ != -1) {
46  err = uv_fs_close(tracing_loop_, &req, fd_, nullptr);
47  CHECK_EQ(err, 0);
48  uv_fs_req_cleanup(&req);
49  }
50  uv_async_send(&exit_signal_);
51  Mutex::ScopedLock scoped_lock(request_mutex_);
52  while (!exited_) {
53  exit_cond_.Wait(scoped_lock);
54  }
55 }
56 
57 void NodeTraceWriter::OpenNewFileForStreaming() {
58  ++file_num_;
59  uv_fs_t req;
60  std::ostringstream log_file;
61  log_file << "node_trace." << file_num_ << ".log";
62  fd_ = uv_fs_open(tracing_loop_, &req, log_file.str().c_str(),
63  O_CREAT | O_WRONLY | O_TRUNC, 0644, NULL);
64  CHECK_NE(fd_, -1);
65  uv_fs_req_cleanup(&req);
66 }
67 
68 void NodeTraceWriter::AppendTraceEvent(TraceObject* trace_event) {
69  Mutex::ScopedLock scoped_lock(stream_mutex_);
70  // If this is the first trace event, open a new file for streaming.
71  if (total_traces_ == 0) {
72  OpenNewFileForStreaming();
73  // Constructing a new JSONTraceWriter object appends "{\"traceEvents\":["
74  // to stream_.
75  // In other words, the constructor initializes the serialization stream
76  // to a state where we can start writing trace events to it.
77  // Repeatedly constructing and destroying json_trace_writer_ allows
78  // us to use V8's JSON writer instead of implementing our own.
79  json_trace_writer_ = TraceWriter::CreateJSONTraceWriter(stream_);
80  }
81  ++total_traces_;
82  json_trace_writer_->AppendTraceEvent(trace_event);
83 }
84 
85 void NodeTraceWriter::FlushPrivate() {
86  std::string str;
87  int highest_request_id;
88  {
89  Mutex::ScopedLock stream_scoped_lock(stream_mutex_);
90  if (total_traces_ >= kTracesPerFile) {
91  total_traces_ = 0;
92  // Destroying the member JSONTraceWriter object appends "]}" to
93  // stream_ - in other words, ending a JSON file.
94  delete json_trace_writer_;
95  }
96  // str() makes a copy of the contents of the stream.
97  str = stream_.str();
98  stream_.str("");
99  stream_.clear();
100  }
101  {
102  Mutex::ScopedLock request_scoped_lock(request_mutex_);
103  highest_request_id = num_write_requests_;
104  }
105  WriteToFile(std::move(str), highest_request_id);
106 }
107 
108 void NodeTraceWriter::FlushSignalCb(uv_async_t* signal) {
109  NodeTraceWriter* trace_writer = static_cast<NodeTraceWriter*>(signal->data);
110  trace_writer->FlushPrivate();
111 }
112 
113 // TODO(matthewloring): Remove (is it necessary to change the API?
114 // Since because of WriteSuffix it no longer matters whether it's true or false)
116  Flush(true);
117 }
118 
119 void NodeTraceWriter::Flush(bool blocking) {
120  Mutex::ScopedLock scoped_lock(request_mutex_);
121  if (!json_trace_writer_) {
122  return;
123  }
124  int request_id = ++num_write_requests_;
125  int err = uv_async_send(&flush_signal_);
126  CHECK_EQ(err, 0);
127  if (blocking) {
128  // Wait until data associated with this request id has been written to disk.
129  // This guarantees that data from all earlier requests have also been
130  // written.
131  while (request_id > highest_request_id_completed_) {
132  request_cond_.Wait(scoped_lock);
133  }
134  }
135 }
136 
137 void NodeTraceWriter::WriteToFile(std::string&& str, int highest_request_id) {
138  WriteRequest* write_req = new WriteRequest();
139  write_req->str = std::move(str);
140  write_req->writer = this;
141  write_req->highest_request_id = highest_request_id;
142  uv_buf_t uv_buf = uv_buf_init(const_cast<char*>(write_req->str.c_str()),
143  write_req->str.length());
144  request_mutex_.Lock();
145  // Manage a queue of WriteRequest objects because the behavior of uv_write is
146  // is undefined if the same WriteRequest object is used more than once
147  // between WriteCb calls. In addition, this allows us to keep track of the id
148  // of the latest write request that actually been completed.
149  write_req_queue_.push(write_req);
150  request_mutex_.Unlock();
151  int err = uv_fs_write(tracing_loop_, reinterpret_cast<uv_fs_t*>(write_req),
152  fd_, &uv_buf, 1, -1, WriteCb);
153  CHECK_EQ(err, 0);
154 }
155 
156 void NodeTraceWriter::WriteCb(uv_fs_t* req) {
157  WriteRequest* write_req = reinterpret_cast<WriteRequest*>(req);
158  CHECK_GE(write_req->req.result, 0);
159 
160  NodeTraceWriter* writer = write_req->writer;
161  int highest_request_id = write_req->highest_request_id;
162  {
163  Mutex::ScopedLock scoped_lock(writer->request_mutex_);
164  CHECK_EQ(write_req, writer->write_req_queue_.front());
165  writer->write_req_queue_.pop();
166  writer->highest_request_id_completed_ = highest_request_id;
167  writer->request_cond_.Broadcast(scoped_lock);
168  }
169  delete write_req;
170 }
171 
172 // static
173 void NodeTraceWriter::ExitSignalCb(uv_async_t* signal) {
174  NodeTraceWriter* trace_writer = static_cast<NodeTraceWriter*>(signal->data);
175  uv_close(reinterpret_cast<uv_handle_t*>(&trace_writer->flush_signal_),
176  nullptr);
177  uv_close(reinterpret_cast<uv_handle_t*>(&trace_writer->exit_signal_),
178  [](uv_handle_t* signal) {
179  NodeTraceWriter* trace_writer =
180  static_cast<NodeTraceWriter*>(signal->data);
181  Mutex::ScopedLock scoped_lock(trace_writer->request_mutex_);
182  trace_writer->exited_ = true;
183  trace_writer->exit_cond_.Signal(scoped_lock);
184  });
185 }
186 
187 } // namespace tracing
188 } // namespace node
void AppendTraceEvent(TraceObject *trace_event) override
void Broadcast(const ScopedLock &)
Definition: node_mutex.h:125
void Wait(const ScopedLock &scoped_lock)
Definition: node_mutex.h:135
uv_fs_t req
Definition: node_file.cc:374
NodeTraceWriter(uv_loop_t *tracing_loop)