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 : // A utility class to manage the RPC session and the associated memory mappings.
16 : #include "syzygy/trace/client/rpc_session.h"
17 :
18 : #include "syzygy/common/com_utils.h"
19 : #include "syzygy/common/rpc/helpers.h"
20 : #include "syzygy/trace/client/client_utils.h"
21 : #include "syzygy/trace/protocol/call_trace_defs.h"
22 : #include "syzygy/trace/rpc/call_trace_rpc.h"
23 :
24 : // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
25 : extern "C" IMAGE_DOS_HEADER __ImageBase;
26 :
27 : namespace trace {
28 : namespace client {
29 :
30 : RpcSession::RpcSession()
31 : : rpc_binding_(NULL),
32 : session_handle_(NULL),
33 : flags_(0),
34 E : is_disabled_(false) {
35 E : }
36 :
37 E : RpcSession::~RpcSession() {
38 E : FreeSharedMemory();
39 E : }
40 :
41 E : bool RpcSession::MapSegmentBuffer(TraceFileSegment* segment) {
42 E : DCHECK(segment != NULL);
43 :
44 : HANDLE mem_handle =
45 E : reinterpret_cast<HANDLE>(segment->buffer_info.shared_memory_handle);
46 :
47 : // Get (or set) the mapping between the handle we've received and the
48 : // corresponding mapped base pointer. Note that the shared_memory_handles_
49 : // map is shared across threads, so we need to hold the shared_memory_lock_
50 : // when we access/update it. This should be the only synchronization point
51 : // in the call trace client library (other than the initial creation of the
52 : // RpcSession object, of course).
53 : {
54 E : base::AutoLock scoped_lock(shared_memory_lock_);
55 :
56 E : uint8*& base_ptr = shared_memory_handles_[mem_handle];
57 E : if (base_ptr == NULL) {
58 : base_ptr = reinterpret_cast<uint8*>(
59 : ::MapViewOfFile(mem_handle, FILE_MAP_WRITE, 0, 0,
60 E : segment->buffer_info.mapping_size));
61 E : if (base_ptr == NULL) {
62 i : DWORD error = ::GetLastError();
63 i : LOG(ERROR) << "Failed to map view of shared memory: "
64 : << ::common::LogWe(error) << ".";
65 i : ignore_result(::CloseHandle(mem_handle));
66 i : shared_memory_handles_.erase(mem_handle);
67 i : return false;
68 : }
69 : }
70 :
71 : segment->base_ptr =
72 E : base_ptr + segment->buffer_info.buffer_offset;
73 E : }
74 :
75 E : segment->header = NULL;
76 E : segment->write_ptr = segment->base_ptr;
77 : segment->end_ptr =
78 E : segment->base_ptr + segment->buffer_info.buffer_size;
79 E : segment->WriteSegmentHeader(session_handle_);
80 :
81 E : DCHECK(segment->header != NULL);
82 :
83 E : return true;
84 E : }
85 :
86 E : bool RpcSession::CreateSession(TraceFileSegment* segment) {
87 E : DCHECK(session_handle_ == NULL);
88 E : DCHECK(rpc_binding_ == NULL);
89 :
90 E : std::wstring protocol;
91 E : std::wstring endpoint;
92 E : ::GetSyzygyCallTraceRpcProtocol(&protocol);
93 E : ::GetSyzygyCallTraceRpcEndpoint(instance_id_, &endpoint);
94 :
95 E : if (!::common::rpc::CreateRpcBinding(protocol, endpoint, &rpc_binding_)) {
96 i : is_disabled_ = true;
97 i : return false;
98 : }
99 :
100 E : DCHECK(rpc_binding_ != 0);
101 :
102 : bool succeeded =
103 : ::common::rpc::InvokeRpc(CallTraceClient_CreateSession, rpc_binding_,
104 : &session_handle_, &segment->buffer_info,
105 E : &flags_).succeeded();
106 :
107 E : if (!succeeded) {
108 : // We often have multiple agents running simultaneously in unittests,
109 : // especially under coverage builds. Detailed logging of which agent was
110 : // trying to connect where helps enormously when tracking down problems.
111 E : base::FilePath module_path;
112 E : CHECK(GetModulePath(&__ImageBase, &module_path));
113 E : LOG(ERROR) << "Failed to create call trace session!";
114 E : LOG(ERROR) << " instance_id = \"" << instance_id_ << "\"";
115 E : LOG(ERROR) << " module = \"" << module_path.value() << "\"";
116 E : is_disabled_ = true;
117 E : return false;
118 : }
119 :
120 E : if ((flags_ & TRACE_FLAG_BATCH_ENTER) != 0) {
121 : // Batch mode is mutually exclusive of all other flags.
122 E : flags_ = TRACE_FLAG_BATCH_ENTER;
123 : }
124 :
125 E : if (!MapSegmentBuffer(segment)) {
126 i : is_disabled_ = true;
127 i : return false;
128 : }
129 :
130 E : return true;
131 E : }
132 :
133 E : bool RpcSession::AllocateBuffer(TraceFileSegment* segment) {
134 E : DCHECK(IsTracing());
135 E : DCHECK(segment != NULL);
136 :
137 : bool succeeded =
138 : ::common::rpc::InvokeRpc(CallTraceClient_AllocateBuffer, session_handle_,
139 E : &segment->buffer_info).succeeded();
140 :
141 E : return succeeded ? MapSegmentBuffer(segment) : false;
142 E : }
143 :
144 E : bool RpcSession::AllocateBuffer(size_t min_size, TraceFileSegment* segment) {
145 E : DCHECK(IsTracing());
146 E : DCHECK(segment != NULL);
147 :
148 : // We want the actual buffer to have the provided minimum size. The call is
149 : // going to prepend the buffer with a RecordPrefix and a
150 : // TraceFileSegmentHeader, so we make room for those.
151 : const size_t kHeaderSize = sizeof(RecordPrefix) +
152 E : sizeof(TraceFileSegmentHeader);
153 :
154 : bool succeeded =
155 : ::common::rpc::InvokeRpc(CallTraceClient_AllocateLargeBuffer,
156 : session_handle_, min_size + kHeaderSize,
157 E : &segment->buffer_info).succeeded();
158 E : if (!succeeded)
159 i : return false;
160 :
161 E : if (!MapSegmentBuffer(segment))
162 i : return false;
163 :
164 : // We want to make sure the mapped buffer has sufficient size.
165 E : DCHECK(segment->CanAllocateRaw(min_size));
166 :
167 E : return true;
168 E : }
169 :
170 E : bool RpcSession::ExchangeBuffer(TraceFileSegment* segment) {
171 E : DCHECK(IsTracing());
172 E : DCHECK(segment != NULL);
173 :
174 : bool succeeded =
175 : ::common::rpc::InvokeRpc(CallTraceClient_ExchangeBuffer, session_handle_,
176 E : &segment->buffer_info).succeeded();
177 :
178 E : return succeeded ? MapSegmentBuffer(segment) : false;
179 E : }
180 :
181 E : bool RpcSession::ReturnBuffer(TraceFileSegment* segment) {
182 E : DCHECK(IsTracing());
183 E : DCHECK(segment != NULL);
184 :
185 : return ::common::rpc::InvokeRpc(CallTraceClient_ReturnBuffer, session_handle_,
186 E : &segment->buffer_info).succeeded();
187 E : }
188 :
189 E : bool RpcSession::CloseSession() {
190 E : DCHECK(IsTracing());
191 :
192 : bool succeeded = ::common::rpc::InvokeRpc(CallTraceClient_CloseSession,
193 E : &session_handle_).succeeded();
194 :
195 E : ignore_result(::RpcBindingFree(&rpc_binding_));
196 E : rpc_binding_ = NULL;
197 :
198 E : return succeeded;
199 E : }
200 :
201 E : void RpcSession::FreeSharedMemory() {
202 E : base::AutoLock scoped_lock_(shared_memory_lock_);
203 :
204 E : if (shared_memory_handles_.empty())
205 E : return;
206 :
207 E : SharedMemoryHandleMap::iterator it = shared_memory_handles_.begin();
208 E : for (; it != shared_memory_handles_.end(); ++it) {
209 E : DCHECK(it->second != NULL);
210 E : if (::UnmapViewOfFile(it->second) == 0) {
211 i : DWORD error = ::GetLastError();
212 i : LOG(WARNING) << "Failed to unmap memory handle: "
213 : << ::common::LogWe(error);
214 : }
215 :
216 E : if (::CloseHandle(it->first) == 0) {
217 i : DWORD error = ::GetLastError();
218 i : LOG(WARNING) << "Failed to close memory handle: "
219 : << ::common::LogWe(error);
220 : }
221 E : }
222 :
223 E : shared_memory_handles_.clear();
224 E : }
225 :
226 : } // namespace client
227 : } // namespace trace
|