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 E : : rpc_binding_(NULL),
32 E : session_handle_(NULL),
33 E : 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_t*& base_ptr = shared_memory_handles_[mem_handle];
57 E : if (base_ptr == NULL) {
58 E : base_ptr = reinterpret_cast<uint8_t*>(::MapViewOfFile(
59 : mem_handle, FILE_MAP_WRITE, 0, 0, segment->buffer_info.mapping_size));
60 E : if (base_ptr == NULL) {
61 i : DWORD error = ::GetLastError();
62 i : LOG(ERROR) << "Failed to map view of shared memory: "
63 : << ::common::LogWe(error) << ".";
64 i : ignore_result(::CloseHandle(mem_handle));
65 i : shared_memory_handles_.erase(mem_handle);
66 i : return false;
67 : }
68 : }
69 :
70 E : segment->base_ptr =
71 : base_ptr + segment->buffer_info.buffer_offset;
72 E : }
73 :
74 E : segment->header = NULL;
75 E : segment->write_ptr = segment->base_ptr;
76 E : segment->end_ptr =
77 : segment->base_ptr + segment->buffer_info.buffer_size;
78 E : segment->WriteSegmentHeader(session_handle_);
79 :
80 E : DCHECK(segment->header != NULL);
81 :
82 E : return true;
83 E : }
84 :
85 E : bool RpcSession::CreateSession(TraceFileSegment* segment) {
86 E : DCHECK(session_handle_ == NULL);
87 E : DCHECK(rpc_binding_ == NULL);
88 :
89 E : std::wstring protocol;
90 E : std::wstring endpoint;
91 E : ::GetSyzygyCallTraceRpcProtocol(&protocol);
92 E : ::GetSyzygyCallTraceRpcEndpoint(instance_id_, &endpoint);
93 :
94 E : if (!::common::rpc::CreateRpcBinding(protocol, endpoint, &rpc_binding_)) {
95 i : is_disabled_ = true;
96 i : return false;
97 : }
98 :
99 E : DCHECK(rpc_binding_ != 0);
100 :
101 : bool succeeded =
102 : ::common::rpc::InvokeRpc(CallTraceClient_CreateSession, rpc_binding_,
103 : &session_handle_, &segment->buffer_info,
104 E : &flags_).succeeded();
105 :
106 E : if (!succeeded) {
107 : // We often have multiple agents running simultaneously in unittests,
108 : // especially under coverage builds. Detailed logging of which agent was
109 : // trying to connect where helps enormously when tracking down problems.
110 E : base::FilePath module_path;
111 E : CHECK(GetModulePath(&__ImageBase, &module_path));
112 E : LOG(ERROR) << "Failed to create call trace session!";
113 E : LOG(ERROR) << " instance_id = \"" << instance_id_ << "\"";
114 E : LOG(ERROR) << " module = \"" << module_path.value() << "\"";
115 E : is_disabled_ = true;
116 E : return false;
117 : }
118 :
119 E : if ((flags_ & TRACE_FLAG_BATCH_ENTER) != 0) {
120 : // Batch mode is mutually exclusive of all other flags.
121 E : flags_ = TRACE_FLAG_BATCH_ENTER;
122 : }
123 :
124 E : if (!MapSegmentBuffer(segment)) {
125 i : is_disabled_ = true;
126 i : return false;
127 : }
128 :
129 E : return true;
130 E : }
131 :
132 E : bool RpcSession::AllocateBuffer(TraceFileSegment* segment) {
133 E : DCHECK(IsTracing());
134 E : DCHECK(segment != NULL);
135 :
136 : bool succeeded =
137 : ::common::rpc::InvokeRpc(CallTraceClient_AllocateBuffer, session_handle_,
138 E : &segment->buffer_info).succeeded();
139 :
140 E : return succeeded ? MapSegmentBuffer(segment) : false;
141 E : }
142 :
143 E : bool RpcSession::AllocateBuffer(size_t min_size, TraceFileSegment* segment) {
144 E : DCHECK(IsTracing());
145 E : DCHECK(segment != NULL);
146 :
147 : // We want the actual buffer to have the provided minimum size. The call is
148 : // going to prepend the buffer with a RecordPrefix and a
149 : // TraceFileSegmentHeader, so we make room for those.
150 E : const size_t kHeaderSize = sizeof(RecordPrefix) +
151 : sizeof(TraceFileSegmentHeader);
152 :
153 : bool succeeded =
154 : ::common::rpc::InvokeRpc(CallTraceClient_AllocateLargeBuffer,
155 : session_handle_, min_size + kHeaderSize,
156 E : &segment->buffer_info).succeeded();
157 E : if (!succeeded)
158 i : return false;
159 :
160 E : if (!MapSegmentBuffer(segment))
161 i : return false;
162 :
163 : // We want to make sure the mapped buffer has sufficient size.
164 E : DCHECK(segment->CanAllocateRaw(min_size));
165 :
166 E : return true;
167 E : }
168 :
169 E : bool RpcSession::ExchangeBuffer(TraceFileSegment* segment) {
170 E : DCHECK(IsTracing());
171 E : DCHECK(segment != NULL);
172 :
173 : bool succeeded =
174 : ::common::rpc::InvokeRpc(CallTraceClient_ExchangeBuffer, session_handle_,
175 E : &segment->buffer_info).succeeded();
176 :
177 E : return succeeded ? MapSegmentBuffer(segment) : false;
178 E : }
179 :
180 E : bool RpcSession::ReturnBuffer(TraceFileSegment* segment) {
181 E : DCHECK(IsTracing());
182 E : DCHECK(segment != NULL);
183 :
184 E : return ::common::rpc::InvokeRpc(CallTraceClient_ReturnBuffer, session_handle_,
185 : &segment->buffer_info).succeeded();
186 E : }
187 :
188 E : bool RpcSession::CloseSession() {
189 E : DCHECK(IsTracing());
190 :
191 : bool succeeded = ::common::rpc::InvokeRpc(CallTraceClient_CloseSession,
192 E : &session_handle_).succeeded();
193 :
194 E : ignore_result(::RpcBindingFree(&rpc_binding_));
195 E : rpc_binding_ = NULL;
196 :
197 E : return succeeded;
198 E : }
199 :
200 E : void RpcSession::FreeSharedMemory() {
201 E : base::AutoLock scoped_lock_(shared_memory_lock_);
202 :
203 E : if (shared_memory_handles_.empty())
204 E : return;
205 :
206 E : SharedMemoryHandleMap::iterator it = shared_memory_handles_.begin();
207 E : for (; it != shared_memory_handles_.end(); ++it) {
208 E : DCHECK(it->second != NULL);
209 E : if (::UnmapViewOfFile(it->second) == 0) {
210 i : DWORD error = ::GetLastError();
211 i : LOG(WARNING) << "Failed to unmap memory handle: "
212 : << ::common::LogWe(error);
213 : }
214 :
215 E : if (::CloseHandle(it->first) == 0) {
216 i : DWORD error = ::GetLastError();
217 i : LOG(WARNING) << "Failed to close memory handle: "
218 : << ::common::LogWe(error);
219 : }
220 E : }
221 :
222 E : shared_memory_handles_.clear();
223 E : }
224 :
225 : } // namespace client
226 : } // namespace trace
|