Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
node_trace_buffer.cc
Go to the documentation of this file.
2 
3 namespace node {
4 namespace tracing {
5 
6 InternalTraceBuffer::InternalTraceBuffer(size_t max_chunks, uint32_t id,
7  NodeTraceWriter* trace_writer)
8  : flushing_(false), max_chunks_(max_chunks),
9  trace_writer_(trace_writer), id_(id) {
10  chunks_.resize(max_chunks);
11 }
12 
13 TraceObject* InternalTraceBuffer::AddTraceEvent(uint64_t* handle) {
14  Mutex::ScopedLock scoped_lock(mutex_);
15  // Create new chunk if last chunk is full or there is no chunk.
16  if (total_chunks_ == 0 || chunks_[total_chunks_ - 1]->IsFull()) {
17  auto& chunk = chunks_[total_chunks_++];
18  if (chunk) {
19  chunk->Reset(current_chunk_seq_++);
20  } else {
21  chunk.reset(new TraceBufferChunk(current_chunk_seq_++));
22  }
23  }
24  auto& chunk = chunks_[total_chunks_ - 1];
25  size_t event_index;
26  TraceObject* trace_object = chunk->AddTraceEvent(&event_index);
27  *handle = MakeHandle(total_chunks_ - 1, chunk->seq(), event_index);
28  return trace_object;
29 }
30 
31 TraceObject* InternalTraceBuffer::GetEventByHandle(uint64_t handle) {
32  Mutex::ScopedLock scoped_lock(mutex_);
33  if (handle == 0) {
34  // A handle value of zero never has a trace event associated with it.
35  return NULL;
36  }
37  size_t chunk_index, event_index;
38  uint32_t buffer_id, chunk_seq;
39  ExtractHandle(handle, &buffer_id, &chunk_index, &chunk_seq, &event_index);
40  if (buffer_id != id_ || chunk_index >= total_chunks_) {
41  // Either the chunk belongs to the other buffer, or is outside the current
42  // range of chunks loaded in memory (the latter being true suggests that
43  // the chunk has already been flushed and is no longer in memory.)
44  return NULL;
45  }
46  auto& chunk = chunks_[chunk_index];
47  if (chunk->seq() != chunk_seq) {
48  // Chunk is no longer in memory.
49  return NULL;
50  }
51  return chunk->GetEventAt(event_index);
52 }
53 
54 void InternalTraceBuffer::Flush(bool blocking) {
55  {
56  Mutex::ScopedLock scoped_lock(mutex_);
57  if (total_chunks_ > 0) {
58  flushing_ = true;
59  for (size_t i = 0; i < total_chunks_; ++i) {
60  auto& chunk = chunks_[i];
61  for (size_t j = 0; j < chunk->size(); ++j) {
62  trace_writer_->AppendTraceEvent(chunk->GetEventAt(j));
63  }
64  }
65  total_chunks_ = 0;
66  flushing_ = false;
67  }
68  }
69  trace_writer_->Flush(blocking);
70 }
71 
72 uint64_t InternalTraceBuffer::MakeHandle(
73  size_t chunk_index, uint32_t chunk_seq, size_t event_index) const {
74  return ((static_cast<uint64_t>(chunk_seq) * Capacity() +
75  chunk_index * TraceBufferChunk::kChunkSize + event_index) << 1) + id_;
76 }
77 
78 void InternalTraceBuffer::ExtractHandle(
79  uint64_t handle, uint32_t* buffer_id, size_t* chunk_index,
80  uint32_t* chunk_seq, size_t* event_index) const {
81  *buffer_id = static_cast<uint32_t>(handle & 0x1);
82  handle >>= 1;
83  *chunk_seq = static_cast<uint32_t>(handle / Capacity());
84  size_t indices = handle % Capacity();
85  *chunk_index = indices / TraceBufferChunk::kChunkSize;
86  *event_index = indices % TraceBufferChunk::kChunkSize;
87 }
88 
90  NodeTraceWriter* trace_writer, uv_loop_t* tracing_loop)
91  : tracing_loop_(tracing_loop), trace_writer_(trace_writer),
92  buffer1_(max_chunks, 0, trace_writer),
93  buffer2_(max_chunks, 1, trace_writer) {
94  current_buf_.store(&buffer1_);
95 
96  flush_signal_.data = this;
97  int err = uv_async_init(tracing_loop_, &flush_signal_,
98  NonBlockingFlushSignalCb);
99  CHECK_EQ(err, 0);
100 
101  exit_signal_.data = this;
102  err = uv_async_init(tracing_loop_, &exit_signal_, ExitSignalCb);
103  CHECK_EQ(err, 0);
104 }
105 
107  uv_async_send(&exit_signal_);
108  Mutex::ScopedLock scoped_lock(exit_mutex_);
109  while (!exited_) {
110  exit_cond_.Wait(scoped_lock);
111  }
112 }
113 
114 TraceObject* NodeTraceBuffer::AddTraceEvent(uint64_t* handle) {
115  // If the buffer is full, attempt to perform a flush.
116  if (!TryLoadAvailableBuffer()) {
117  // Assign a value of zero as the trace event handle.
118  // This is equivalent to calling InternalTraceBuffer::MakeHandle(0, 0, 0),
119  // and will cause GetEventByHandle to return NULL if passed as an argument.
120  *handle = 0;
121  return nullptr;
122  }
123  return current_buf_.load()->AddTraceEvent(handle);
124 }
125 
126 TraceObject* NodeTraceBuffer::GetEventByHandle(uint64_t handle) {
127  return current_buf_.load()->GetEventByHandle(handle);
128 }
129 
131  buffer1_.Flush(true);
132  buffer2_.Flush(true);
133  return true;
134 }
135 
136 // Attempts to set current_buf_ such that it references a buffer that can
137 // can write at least one trace event. If both buffers are unavailable this
138 // method returns false; otherwise it returns true.
139 bool NodeTraceBuffer::TryLoadAvailableBuffer() {
140  InternalTraceBuffer* prev_buf = current_buf_.load();
141  if (prev_buf->IsFull()) {
142  uv_async_send(&flush_signal_); // trigger flush on a separate thread
143  InternalTraceBuffer* other_buf = prev_buf == &buffer1_ ?
144  &buffer2_ : &buffer1_;
145  if (!other_buf->IsFull()) {
146  current_buf_.store(other_buf);
147  } else {
148  return false;
149  }
150  }
151  return true;
152 }
153 
154 // static
155 void NodeTraceBuffer::NonBlockingFlushSignalCb(uv_async_t* signal) {
156  NodeTraceBuffer* buffer = reinterpret_cast<NodeTraceBuffer*>(signal->data);
157  if (buffer->buffer1_.IsFull() && !buffer->buffer1_.IsFlushing()) {
158  buffer->buffer1_.Flush(false);
159  }
160  if (buffer->buffer2_.IsFull() && !buffer->buffer2_.IsFlushing()) {
161  buffer->buffer2_.Flush(false);
162  }
163 }
164 
165 // static
166 void NodeTraceBuffer::ExitSignalCb(uv_async_t* signal) {
167  NodeTraceBuffer* buffer = reinterpret_cast<NodeTraceBuffer*>(signal->data);
168  uv_close(reinterpret_cast<uv_handle_t*>(&buffer->flush_signal_), nullptr);
169  uv_close(reinterpret_cast<uv_handle_t*>(&buffer->exit_signal_),
170  [](uv_handle_t* signal) {
171  NodeTraceBuffer* buffer =
172  reinterpret_cast<NodeTraceBuffer*>(signal->data);
173  Mutex::ScopedLock scoped_lock(buffer->exit_mutex_);
174  buffer->exited_ = true;
175  buffer->exit_cond_.Signal(scoped_lock);
176  });
177 }
178 
179 } // namespace tracing
180 } // namespace node
TraceObject * GetEventByHandle(uint64_t handle) override
InternalTraceBuffer(size_t max_chunks, uint32_t id, NodeTraceWriter *trace_writer)
void AppendTraceEvent(TraceObject *trace_event) override
TraceObject * GetEventByHandle(uint64_t handle)
TraceObject * AddTraceEvent(uint64_t *handle) override
void Wait(const ScopedLock &scoped_lock)
Definition: node_mutex.h:135
TraceObject * AddTraceEvent(uint64_t *handle)
NodeTraceBuffer(size_t max_chunks, NodeTraceWriter *trace_writer, uv_loop_t *tracing_loop)