1 : // Copyright 2012 Google Inc. 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 : // This file defines the trace::service::Service class which
16 : // implements the call trace service RPC interface.
17 : //
18 : // TODO(rogerm): Use server controlled context handles to refer to the buffers
19 : // across the RPC boundary. The shared memory handle is client controlled
20 : // and not necessarily unique.
21 :
22 : #include "syzygy/trace/service/service.h"
23 :
24 : #include "base/bind.h"
25 : #include "base/callback.h"
26 : #include "base/string_util.h"
27 : #include "base/memory/scoped_ptr.h"
28 : #include "sawbuck/common/com_utils.h"
29 : #include "syzygy/common/align.h"
30 : #include "syzygy/trace/protocol/call_trace_defs.h"
31 : #include "syzygy/trace/service/buffer_consumer.h"
32 : #include "syzygy/trace/service/session.h"
33 :
34 : namespace trace {
35 : namespace service {
36 :
37 : const size_t Service::kDefaultBufferSize = 2 * 1024 * 1024;
38 : const size_t Service::kDefaultNumIncrementalBuffers = 16;
39 :
40 : // The choice of this value is not particularly important, but it should be
41 : // something that is relatively prime to the number of buffers created per
42 : // allocation, and it should represent more memory than our disk bandwidth
43 : // can reasonably write in about a second or so, so as to allow sufficient
44 : // buffering for smoothing. Assuming 20MB/sec consistent throughput, this
45 : // represents about 26 MB, so 1.3 seconds of disk bandwidth.
46 : const size_t Service::kDefaultMaxBuffersPendingWrite = 13;
47 :
48 : Service::Service(BufferConsumerFactory* factory)
49 : : num_active_sessions_(0),
50 : num_incremental_buffers_(kDefaultNumIncrementalBuffers),
51 : buffer_size_in_bytes_(kDefaultBufferSize),
52 : max_buffers_pending_write_(kDefaultMaxBuffersPendingWrite),
53 : owner_thread_(base::PlatformThread::CurrentId()),
54 : buffer_consumer_factory_(factory),
55 : a_session_has_closed_(&lock_),
56 : rpc_is_initialized_(false),
57 : rpc_is_running_(false),
58 : rpc_is_non_blocking_(false),
59 E : flags_(TRACE_FLAG_BATCH_ENTER) {
60 E : DCHECK(factory != NULL);
61 E : }
62 :
63 E : Service::~Service() {
64 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
65 E : DCHECK(buffer_consumer_factory_ != NULL);
66 :
67 E : Stop();
68 :
69 E : DCHECK(sessions_.empty());
70 E : DCHECK_EQ(0U, num_active_sessions_);
71 E : }
72 :
73 E : void Service::AddOneActiveSession() {
74 E : base::AutoLock auto_lock(lock_);
75 :
76 E : ++num_active_sessions_;
77 E : }
78 :
79 E : void Service::RemoveOneActiveSession() {
80 : {
81 E : base::AutoLock auto_lock(lock_);
82 E : DCHECK_LT(0u, num_active_sessions_);
83 :
84 E : --num_active_sessions_;
85 E : }
86 :
87 E : a_session_has_closed_.Signal();
88 E : }
89 :
90 E : bool Service::OpenServiceEvent() {
91 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
92 E : DCHECK(!service_event_.IsValid());
93 :
94 E : std::wstring event_name;
95 E : ::GetSyzygyCallTraceRpcEventName(instance_id_, &event_name);
96 :
97 E : service_event_.Set(::CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
98 E : if (!service_event_.IsValid()) {
99 E : DWORD error = ::GetLastError();
100 E : LOG(ERROR) << "Failed to create event: " << com::LogWe(error) << ".";
101 E : return false;
102 : }
103 :
104 E : return true;
105 E : }
106 :
107 E : bool Service::AcquireServiceMutex() {
108 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
109 E : DCHECK(!service_mutex_.IsValid());
110 :
111 E : std::wstring mutex_name;
112 E : ::GetSyzygyCallTraceRpcMutexName(instance_id_, &mutex_name);
113 E : base::win::ScopedHandle mutex(::CreateMutex(NULL, FALSE, mutex_name.c_str()));
114 E : if (!mutex.IsValid()) {
115 i : DWORD error = ::GetLastError();
116 i : LOG(ERROR) << "Failed to create mutex: " << com::LogWe(error) << ".";
117 i : return false;
118 : }
119 E : const DWORD kOneSecondInMs = 1000;
120 :
121 E : switch (::WaitForSingleObject(mutex, kOneSecondInMs)) {
122 : case WAIT_ABANDONED:
123 i : LOG(WARNING) << "Orphaned service mutex found!";
124 : // Fall through...
125 :
126 : case WAIT_OBJECT_0:
127 E : VLOG(1) << "Service mutex acquired.";
128 E : service_mutex_.Set(mutex.Take());
129 E : return true;
130 :
131 : case WAIT_TIMEOUT:
132 E : LOG(ERROR) << "Another instance of the service is running.";
133 E : break;
134 :
135 : default: {
136 i : DWORD error = ::GetLastError();
137 i : LOG(ERROR) << "Failed to acquire mutex: " << com::LogWe(error) << ".";
138 : break;
139 : }
140 : }
141 E : return false;
142 E : }
143 :
144 E : void Service::ReleaseServiceMutex() {
145 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
146 :
147 E : if (service_mutex_.IsValid()) {
148 E : ::ReleaseMutex(service_mutex_);
149 E : service_mutex_.Close();
150 : }
151 E : }
152 :
153 E : bool Service::InitializeRpc() {
154 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
155 :
156 E : if (rpc_is_initialized_) {
157 i : LOG(WARNING) << "The call trace service RPC stack is already initialized.";
158 i : return true;
159 : }
160 :
161 E : RPC_STATUS status = RPC_S_OK;
162 :
163 : // Initialize the RPC protocol we want to use.
164 E : std::wstring protocol;
165 E : std::wstring endpoint;
166 E : ::GetSyzygyCallTraceRpcProtocol(&protocol);
167 E : ::GetSyzygyCallTraceRpcEndpoint(instance_id_, &endpoint);
168 :
169 E : VLOG(1) << "Initializing RPC endpoint '" << endpoint << "' "
170 : << "using the '" << protocol << "' protocol.";
171 : status = ::RpcServerUseProtseqEp(
172 : reinterpret_cast<RPC_WSTR>(&protocol[0]),
173 : RPC_C_LISTEN_MAX_CALLS_DEFAULT,
174 : reinterpret_cast<RPC_WSTR>(&endpoint[0]),
175 E : NULL /* Security descriptor. */);
176 E : if (status != RPC_S_OK && status != RPC_S_DUPLICATE_ENDPOINT) {
177 i : LOG(ERROR) << "Failed to init RPC protocol: " << com::LogWe(status) << ".";
178 i : return false;
179 : }
180 :
181 : // Register the server version of the CallTrace interface.
182 E : VLOG(1) << "Registering the CallTrace interface.";
183 : status = ::RpcServerRegisterIf(
184 E : CallTraceService_CallTrace_v1_0_s_ifspec, NULL, NULL);
185 E : if (status != RPC_S_OK) {
186 i : LOG(ERROR) << "Failed to register CallTrace RPC interface: "
187 : << com::LogWe(status) << ".";
188 i : return false;
189 : }
190 :
191 : // Register the server version of the CallTraceControl interface.
192 E : VLOG(1) << "Registering the CallTraceControl interface.";
193 : status = ::RpcServerRegisterIf(
194 E : CallTraceService_CallTraceControl_v1_0_s_ifspec, NULL, NULL);
195 E : if (status != RPC_S_OK) {
196 i : LOG(ERROR) << "Failed to register CallTraceControl RPC interface: "
197 : << com::LogWe(status) << ".";
198 i : return false;
199 : }
200 :
201 E : rpc_is_initialized_ = true;
202 E : return true;
203 E : }
204 :
205 E : bool Service::RunRPC(bool non_blocking) {
206 E : VLOG(1) << "Starting the RPC server.";
207 :
208 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
209 :
210 E : if (rpc_is_running_) {
211 i : LOG(ERROR) << "The RPC server is already running.";
212 i : return false;
213 : }
214 :
215 E : rpc_is_running_ = true;
216 E : rpc_is_non_blocking_ = non_blocking;
217 :
218 : RPC_STATUS status = ::RpcServerListen(
219 : 1, // Minimum number of handler threads.
220 : RPC_C_LISTEN_MAX_CALLS_DEFAULT,
221 E : TRUE);
222 :
223 E : if (status != RPC_S_OK)
224 i : LOG(ERROR) << "Failed to run RPC server: " << com::LogWe(status) << ".";
225 :
226 E : if (status == RPC_S_OK) {
227 : // Signal that the service is up and running.
228 E : DCHECK(service_event_.IsValid());
229 E : BOOL success = ::SetEvent(service_event_.Get());
230 E : DCHECK_EQ(TRUE, success);
231 :
232 : // Wait here if we're in blocking mode.
233 E : if (!non_blocking) {
234 E : VLOG(1) << "Call-trace service is running in blocking mode.";
235 E : status = RpcMgmtWaitServerListen();
236 :
237 E : if (status == RPC_S_OK) {
238 E : VLOG(1) << "Call-trace service has finished accepting requests.";
239 E : } else {
240 i : LOG(ERROR) << "Failed to wait on RPC server: "
241 : << com::LogWe(status) << ".";
242 : }
243 : }
244 : }
245 :
246 E : if (status != RPC_S_OK) {
247 i : rpc_is_running_ = false;
248 i : rpc_is_non_blocking_ = false;
249 i : return false;
250 : }
251 :
252 E : if (rpc_is_non_blocking_)
253 E : VLOG(1) << "Call-trace service is running in non-blocking mode.";
254 :
255 E : return true;
256 E : }
257 :
258 E : void Service::StopRpc() {
259 E : if (!rpc_is_running_)
260 E : return;
261 :
262 : // Stop the RPC Server.
263 E : base::AutoLock auto_lock(lock_);
264 E : if (rpc_is_running_) {
265 E : VLOG(1) << "Stopping RPC server.";
266 E : RPC_STATUS status = ::RpcMgmtStopServerListening(NULL);
267 E : if (status != RPC_S_OK) {
268 i : LOG(ERROR) << "Failed to stop the RPC server: "
269 : << com::LogWe(status) << ".";
270 : }
271 E : rpc_is_running_ = false;
272 : }
273 E : }
274 :
275 E : void Service::CleanupRpc() {
276 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
277 E : DCHECK(rpc_is_running_ == false);
278 :
279 E : RPC_STATUS status = RPC_S_OK;
280 :
281 : // If we're running in non-blocking mode, then we have to wait for
282 : // any in-flight RPC requests to terminate.
283 E : if (rpc_is_non_blocking_) {
284 E : VLOG(1) << "Waiting for outstanding RPC requests to terminate.";
285 E : status = ::RpcMgmtWaitServerListen();
286 E : if (status != RPC_S_OK && status != RPC_S_NOT_LISTENING) {
287 i : LOG(ERROR) << "Failed wait for RPC server shutdown: "
288 : << com::LogWe(status) << ".";
289 : }
290 E : rpc_is_non_blocking_ = false;
291 : }
292 :
293 : // Unregister the RPC interfaces.
294 E : if (rpc_is_initialized_) {
295 E : VLOG(1) << "Unregistering RPC interfaces.";
296 E : status = ::RpcServerUnregisterIf(NULL, NULL, FALSE);
297 E : if (status != RPC_S_OK) {
298 i : LOG(ERROR) << "Failed to unregister RPC interfaces: "
299 : << com::LogWe(status) << ".";
300 : }
301 E : rpc_is_initialized_ = false;
302 : }
303 E : }
304 :
305 E : bool Service::Start(bool non_blocking) {
306 E : LOG(INFO) << "Starting the call-trace service.";
307 :
308 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
309 :
310 E : if (!AcquireServiceMutex())
311 E : return false;
312 :
313 E : if (!OpenServiceEvent())
314 E : return false;
315 :
316 E : if (!InitializeRpc()) {
317 i : ReleaseServiceMutex();
318 i : return false;
319 : }
320 :
321 E : LOG(INFO) << "The call-trace service is running.";
322 :
323 E : if (!RunRPC(non_blocking))
324 i : return false;
325 :
326 E : LOG(INFO) << "The call-trace service is no longer running.";
327 :
328 E : return true;
329 E : }
330 :
331 E : bool Service::Stop() {
332 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
333 :
334 E : LOG(INFO) << "Stopping the call-trace service.";
335 :
336 E : StopRpc();
337 E : CleanupRpc();
338 E : CloseAllOpenSessions();
339 E : ReleaseServiceMutex();
340 :
341 : // Signal that we've shut down.
342 E : if (service_event_.IsValid())
343 E : ::ResetEvent(service_event_.Get());
344 :
345 E : LOG(INFO) << "The call-trace service is stopped.";
346 E : return true;
347 E : }
348 :
349 E : bool Service::CloseAllOpenSessions() {
350 E : DCHECK_EQ(owner_thread_, base::PlatformThread::CurrentId());
351 E : DCHECK(!rpc_is_running_);
352 :
353 E : VLOG(1) << "Flushing all outstanding buffers.";
354 :
355 E : SessionMap to_close;
356 : {
357 E : base::AutoLock auto_lock(lock_);
358 E : to_close.swap(sessions_);
359 :
360 E : DCHECK(sessions_.empty());
361 E : }
362 :
363 : // Tell each session that they are to be closed. This will get them to
364 : // flush all outstanding buffers to their respective consumers.
365 E : SessionMap::iterator iter = to_close.begin();
366 E : for (; iter != to_close.end(); ++iter) {
367 E : iter->second->Close();
368 E : }
369 :
370 : // Release the references we hold to the closing sessions.
371 E : to_close.clear();
372 :
373 : // Wait until all pending sessions have closed.
374 : {
375 E : base::AutoLock auto_lock(lock_);
376 :
377 E : int pending_sessions = 0;
378 E : while ((pending_sessions = num_active_sessions_) != 0) {
379 E : VLOG(1) << "There are " << pending_sessions << " pending sessions.";
380 E : a_session_has_closed_.Wait();
381 E : }
382 E : }
383 :
384 E : return true;
385 E : }
386 :
387 E : Session* Service::CreateSession() {
388 E : return new Session(this);
389 E : }
390 :
391 : // RPC entry point.
392 E : bool Service::RequestShutdown() {
393 E : VLOG(1) << "Requesting a shutdown of the call trace service.";
394 :
395 E : StopRpc();
396 :
397 E : return true;
398 E : }
399 :
400 : // RPC entry point.
401 : bool Service::CreateSession(handle_t binding,
402 : SessionHandle* session_handle,
403 : CallTraceBuffer* call_trace_buffer,
404 E : unsigned long* flags) {
405 : if (binding == NULL || session_handle == NULL || call_trace_buffer == NULL ||
406 E : flags == NULL) {
407 i : LOG(WARNING) << "Invalid RPC parameters.";
408 i : return false;
409 : }
410 E : const int kVersion = 2;
411 E : RPC_CALL_ATTRIBUTES_V2 attribs = { kVersion, RPC_QUERY_CLIENT_PID };
412 E : RPC_STATUS status = RpcServerInqCallAttributes(binding, &attribs);
413 E : if (status != RPC_S_OK) {
414 i : LOG(ERROR) << "Failed to query RPC call attributes: "
415 : << com::LogWe(status) << ".";
416 i : return false;
417 : }
418 :
419 E : ProcessId client_process_id = reinterpret_cast<ProcessId>(attribs.ClientPID);
420 :
421 E : VLOG(1) << "Registering client process PID=" << client_process_id << ".";
422 :
423 E : scoped_refptr<Session> session;
424 E : if (!GetNewSession(client_process_id, &session))
425 i : return false;
426 :
427 E : DCHECK(session.get() != NULL);
428 :
429 : // Request a buffer for the client.
430 E : Buffer* client_buffer = NULL;
431 E : if (!session->GetNextBuffer(&client_buffer)) {
432 i : sessions_.erase(session->client_process_id());
433 i : session->Close();
434 i : return false;
435 : }
436 E : DCHECK(client_buffer != NULL);
437 :
438 : // Copy buffer info into the RPC struct, slicing off the private bits.
439 E : *session_handle = reinterpret_cast<SessionHandle>(session.get());
440 E : *call_trace_buffer = *client_buffer;
441 E : *flags = flags_;
442 :
443 E : return true;
444 E : }
445 :
446 : // RPC entry point.
447 : bool Service::AllocateBuffer(SessionHandle session_handle,
448 E : CallTraceBuffer* call_trace_buffer) {
449 E : if (session_handle == NULL || call_trace_buffer == NULL) {
450 i : LOG(WARNING) << "Invalid RPC parameters.";
451 i : return false;
452 : }
453 :
454 E : scoped_refptr<Session> session;
455 E : if (!GetExistingSession(session_handle, &session))
456 i : return false;
457 E : DCHECK(session.get() != NULL);
458 :
459 : // Request a buffer for the client.
460 E : Buffer* client_buffer = NULL;
461 E : if (!session->GetNextBuffer(&client_buffer))
462 i : return false;
463 :
464 : // Copy buffer info into the RPC struct, slicing off the private bits.
465 E : DCHECK(client_buffer != NULL);
466 E : *call_trace_buffer = *client_buffer;
467 :
468 E : return true;
469 E : }
470 :
471 : // RPC entry point.
472 : bool Service::AllocateLargeBuffer(SessionHandle session_handle,
473 : size_t minimum_size,
474 E : CallTraceBuffer* call_trace_buffer) {
475 E : if (session_handle == NULL || call_trace_buffer == NULL) {
476 i : LOG(WARNING) << "Invalid RPC parameters.";
477 i : return false;
478 : }
479 :
480 E : scoped_refptr<Session> session;
481 E : if (!GetExistingSession(session_handle, &session))
482 i : return false;
483 E : DCHECK(session.get() != NULL);
484 :
485 : // Request a buffer for the client.
486 E : Buffer* client_buffer = NULL;
487 E : if (!session->GetBuffer(minimum_size, &client_buffer))
488 i : return false;
489 :
490 : // Copy buffer info into the RPC struct, slicing off the private bits.
491 E : DCHECK(client_buffer != NULL);
492 E : *call_trace_buffer = *client_buffer;
493 :
494 E : return true;
495 E : }
496 :
497 : // RPC entry point.
498 : bool Service::CommitAndExchangeBuffer(SessionHandle session_handle,
499 : CallTraceBuffer* call_trace_buffer,
500 E : ExchangeFlag perform_exchange) {
501 E : if (session_handle == NULL || call_trace_buffer == NULL) {
502 i : LOG(WARNING) << "Invalid RPC parameters.";
503 i : return false;
504 : }
505 :
506 : DCHECK(perform_exchange == PERFORM_EXCHANGE ||
507 E : perform_exchange == DO_NOT_PERFORM_EXCHANGE);
508 :
509 E : bool result = true;
510 E : scoped_refptr<Session> session;
511 E : if (!GetExistingSession(session_handle, &session))
512 i : return false;
513 E : DCHECK(session.get() != NULL);
514 :
515 E : Buffer* buffer = NULL;
516 E : if (!session->FindBuffer(call_trace_buffer, &buffer))
517 i : return false;
518 :
519 E : DCHECK(buffer != NULL);
520 :
521 : // We can't say anything about the buffer's state, as it possible that the
522 : // session that owns it has already been asked to shutdown, in which case
523 : // all of its buffers have already been scheduled for writing and the call
524 : // below will be ignored.
525 :
526 : // Return the buffer to the session. The session will then take care of
527 : // scheduling it for writing. Currently, it feeds it right back to us, but
528 : // this routing allows the write-queue to be decoupled from the service
529 : // more easily in the future.
530 E : if (!session->ReturnBuffer(buffer)) {
531 i : LOG(ERROR) << "Unable to return buffer to session.";
532 i : return false;
533 : }
534 :
535 E : ZeroMemory(call_trace_buffer, sizeof(*call_trace_buffer));
536 :
537 E : if (perform_exchange == PERFORM_EXCHANGE) {
538 : // Request a buffer for the client.
539 E : Buffer* client_buffer = NULL;
540 E : if (!session->GetNextBuffer(&client_buffer)) {
541 i : result = false;
542 i : } else {
543 : // Copy buffer info into the RPC struct, slicing off the private bits.
544 E : DCHECK(client_buffer != NULL);
545 E : *call_trace_buffer = *client_buffer;
546 : }
547 : }
548 :
549 E : return result;
550 E : }
551 :
552 : // RPC entry-point.
553 E : bool Service::CloseSession(SessionHandle* session_handle) {
554 E : if (session_handle == NULL || *session_handle == NULL) {
555 i : LOG(WARNING) << "Invalid RPC parameters.";
556 i : return false;
557 : }
558 :
559 E : scoped_refptr<Session> session;
560 : {
561 E : base::AutoLock auto_lock(lock_);
562 :
563 E : if (!GetExistingSessionUnlocked(*session_handle, &session))
564 i : return false;
565 :
566 E : size_t num_erased = sessions_.erase(session->client_process_id());
567 E : DCHECK_EQ(1U, num_erased);
568 E : }
569 :
570 E : DCHECK(session.get() != NULL);
571 :
572 : // Signal that we want the session to close. This will cause it to
573 : // schedule all of its outstanding buffers for writing. It will destroy
574 : // itself once it's reference count drops to zero.
575 E : session->Close();
576 :
577 E : *session_handle = NULL;
578 :
579 E : return true;
580 E : }
581 :
582 : bool Service::GetNewSession(ProcessId client_process_id,
583 E : scoped_refptr<Session>* session) {
584 E : DCHECK(session != NULL);
585 E : *session = NULL;
586 :
587 : // Create the new session.
588 E : scoped_refptr<Session> new_session(CreateSession());
589 E : if (new_session.get() == NULL)
590 i : return false;
591 :
592 : // Initialize the session.
593 E : if (!new_session->Init(client_process_id))
594 i : return false;
595 :
596 : // Allocate a new buffer consumer.
597 E : scoped_refptr<BufferConsumer> consumer;
598 E : if (!buffer_consumer_factory_->CreateConsumer(&consumer))
599 i : return false;
600 :
601 : // Open the buffer consumer.
602 E : if (!consumer->Open(new_session))
603 i : return false;
604 :
605 : // Hand the buffer consumer over to the session. The session will direct
606 : // returned buffers to the consumer.
607 E : new_session->set_buffer_consumer(consumer);
608 :
609 E : bool inserted = false;
610 : {
611 E : base::AutoLock auto_lock(lock_);
612 : // Attempt to add the session to the session map.
613 : inserted = sessions_.insert(
614 E : SessionMap::value_type(client_process_id, new_session)).second;
615 E : }
616 :
617 E : if (inserted == false) {
618 i : LOG(ERROR) << "A session already exists for process " << client_process_id
619 : << ".";
620 i : consumer->Close(new_session.get());
621 i : CHECK(new_session->Close());
622 :
623 i : return false;
624 : }
625 :
626 : // The session map has taken ownership of the session object; release
627 : // and return the session pointer.
628 E : *session = new_session;
629 :
630 E : return true;
631 E : }
632 :
633 : bool Service::GetExistingSession(SessionHandle session_handle,
634 E : scoped_refptr<Session>* session) {
635 E : DCHECK(session != NULL);
636 E : base::AutoLock auto_lock(lock_);
637 :
638 E : return GetExistingSessionUnlocked(session_handle, session);
639 E : }
640 :
641 : bool Service::GetExistingSessionUnlocked(SessionHandle session_handle,
642 E : scoped_refptr<Session>* session) {
643 E : DCHECK(session != NULL);
644 E : lock_.AssertAcquired();
645 :
646 E : *session = reinterpret_cast<Session*>(session_handle);
647 :
648 : #ifndef NDEBUG
649 E : if (sessions_.find((*session)->client_process_id()) == sessions_.end()) {
650 i : LOG(ERROR) << "No session exists for handle " << session_handle << ".";
651 i : *session = static_cast<Session*>(NULL);
652 i : return false;
653 : }
654 : #endif
655 :
656 E : return true;
657 E : }
658 :
659 : } // namespace service
660 : } // namespace trace
|