1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/bootstrap_natives.h" |
6 | #include "vm/dart_api_impl.h" |
7 | #include "vm/datastream.h" |
8 | #include "vm/exceptions.h" |
9 | #include "vm/flags.h" |
10 | #include "vm/growable_array.h" |
11 | #include "vm/kernel_isolate.h" |
12 | #include "vm/message.h" |
13 | #include "vm/message_handler.h" |
14 | #include "vm/native_entry.h" |
15 | #include "vm/object.h" |
16 | #include "vm/port.h" |
17 | #include "vm/service_event.h" |
18 | #include "vm/service_isolate.h" |
19 | #include "vm/symbols.h" |
20 | |
21 | namespace dart { |
22 | |
23 | DECLARE_FLAG(bool, trace_service); |
24 | |
25 | #ifndef PRODUCT |
26 | class RegisterRunningIsolatesVisitor : public IsolateVisitor { |
27 | public: |
28 | explicit RegisterRunningIsolatesVisitor(Thread* thread) |
29 | : IsolateVisitor(), |
30 | register_function_(Function::Handle(thread->zone())), |
31 | service_isolate_(thread->isolate()) { |
32 | ASSERT(ServiceIsolate::IsServiceIsolate(Isolate::Current())); |
33 | // Get library. |
34 | const String& library_url = Symbols::DartVMService(); |
35 | ASSERT(!library_url.IsNull()); |
36 | const Library& library = |
37 | Library::Handle(Library::LookupLibrary(thread, library_url)); |
38 | ASSERT(!library.IsNull()); |
39 | // Get function. |
40 | const String& function_name = |
41 | String::Handle(String::New("_registerIsolate" )); |
42 | ASSERT(!function_name.IsNull()); |
43 | register_function_ = library.LookupFunctionAllowPrivate(function_name); |
44 | ASSERT(!register_function_.IsNull()); |
45 | } |
46 | |
47 | virtual void VisitIsolate(Isolate* isolate) { |
48 | ASSERT(ServiceIsolate::IsServiceIsolate(Isolate::Current())); |
49 | if (!FLAG_show_invisible_isolates && IsVMInternalIsolate(isolate)) { |
50 | // We do not register the service (and descendants), the vm-isolate, or |
51 | // the kernel isolate. |
52 | return; |
53 | } |
54 | // Setup arguments for call. |
55 | Dart_Port port_id = isolate->main_port(); |
56 | const Integer& port_int = Integer::Handle(Integer::New(port_id)); |
57 | ASSERT(!port_int.IsNull()); |
58 | const SendPort& send_port = SendPort::Handle(SendPort::New(port_id)); |
59 | const String& name = String::Handle(String::New(isolate->name())); |
60 | ASSERT(!name.IsNull()); |
61 | const Array& args = Array::Handle(Array::New(3)); |
62 | ASSERT(!args.IsNull()); |
63 | args.SetAt(0, port_int); |
64 | args.SetAt(1, send_port); |
65 | args.SetAt(2, name); |
66 | const Object& r = |
67 | Object::Handle(DartEntry::InvokeFunction(register_function_, args)); |
68 | if (FLAG_trace_service) { |
69 | OS::PrintErr("vm-service: Isolate %s %" Pd64 " registered.\n" , |
70 | name.ToCString(), port_id); |
71 | } |
72 | ASSERT(!r.IsError()); |
73 | } |
74 | |
75 | private: |
76 | Function& register_function_; |
77 | Isolate* service_isolate_; |
78 | }; |
79 | #endif // !PRODUCT |
80 | |
81 | DEFINE_NATIVE_ENTRY(VMService_SendIsolateServiceMessage, 0, 2) { |
82 | #ifndef PRODUCT |
83 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, sp, arguments->NativeArgAt(0)); |
84 | GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(1)); |
85 | |
86 | // Set the type of the OOB message. |
87 | message.SetAt(0, |
88 | Smi::Handle(thread->zone(), Smi::New(Message::kServiceOOBMsg))); |
89 | |
90 | // Serialize message. |
91 | MessageWriter writer(false); |
92 | // TODO(turnidge): Throw an exception when the return value is false? |
93 | bool result = PortMap::PostMessage( |
94 | writer.WriteMessage(message, sp.Id(), Message::kOOBPriority)); |
95 | return Bool::Get(result).raw(); |
96 | #else |
97 | return Object::null(); |
98 | #endif |
99 | } |
100 | |
101 | DEFINE_NATIVE_ENTRY(VMService_SendRootServiceMessage, 0, 1) { |
102 | #ifndef PRODUCT |
103 | GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(0)); |
104 | return Service::HandleRootMessage(message); |
105 | #endif |
106 | return Object::null(); |
107 | } |
108 | |
109 | DEFINE_NATIVE_ENTRY(VMService_SendObjectRootServiceMessage, 0, 1) { |
110 | #ifndef PRODUCT |
111 | GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(0)); |
112 | return Service::HandleObjectRootMessage(message); |
113 | #endif |
114 | return Object::null(); |
115 | } |
116 | |
117 | DEFINE_NATIVE_ENTRY(VMService_OnStart, 0, 0) { |
118 | #ifndef PRODUCT |
119 | if (FLAG_trace_service) { |
120 | OS::PrintErr("vm-service: Booting dart:vmservice library.\n" ); |
121 | } |
122 | // Boot the dart:vmservice library. |
123 | ServiceIsolate::BootVmServiceLibrary(); |
124 | // Register running isolates with service. |
125 | RegisterRunningIsolatesVisitor register_isolates(thread); |
126 | if (FLAG_trace_service) { |
127 | OS::PrintErr("vm-service: Registering running isolates.\n" ); |
128 | } |
129 | Isolate::VisitIsolates(®ister_isolates); |
130 | #endif |
131 | return Object::null(); |
132 | } |
133 | |
134 | DEFINE_NATIVE_ENTRY(VMService_OnExit, 0, 0) { |
135 | #ifndef PRODUCT |
136 | if (FLAG_trace_service) { |
137 | OS::PrintErr("vm-service: processed exit message.\n" ); |
138 | MessageHandler* message_handler = isolate->message_handler(); |
139 | OS::PrintErr("vm-service: live ports = %" Pd "\n" , |
140 | message_handler->live_ports()); |
141 | } |
142 | #endif |
143 | return Object::null(); |
144 | } |
145 | |
146 | DEFINE_NATIVE_ENTRY(VMService_OnServerAddressChange, 0, 1) { |
147 | #ifndef PRODUCT |
148 | GET_NATIVE_ARGUMENT(String, address, arguments->NativeArgAt(0)); |
149 | if (address.IsNull()) { |
150 | ServiceIsolate::SetServerAddress(NULL); |
151 | } else { |
152 | ServiceIsolate::SetServerAddress(address.ToCString()); |
153 | } |
154 | #endif |
155 | return Object::null(); |
156 | } |
157 | |
158 | DEFINE_NATIVE_ENTRY(VMService_ListenStream, 0, 1) { |
159 | #ifndef PRODUCT |
160 | GET_NON_NULL_NATIVE_ARGUMENT(String, stream_id, arguments->NativeArgAt(0)); |
161 | bool result = Service::ListenStream(stream_id.ToCString()); |
162 | return Bool::Get(result).raw(); |
163 | #else |
164 | return Object::null(); |
165 | #endif |
166 | } |
167 | |
168 | DEFINE_NATIVE_ENTRY(VMService_CancelStream, 0, 1) { |
169 | #ifndef PRODUCT |
170 | GET_NON_NULL_NATIVE_ARGUMENT(String, stream_id, arguments->NativeArgAt(0)); |
171 | Service::CancelStream(stream_id.ToCString()); |
172 | #endif |
173 | return Object::null(); |
174 | } |
175 | |
176 | DEFINE_NATIVE_ENTRY(VMService_RequestAssets, 0, 0) { |
177 | #ifndef PRODUCT |
178 | return Service::RequestAssets(); |
179 | #else |
180 | return Object::null(); |
181 | #endif |
182 | } |
183 | |
184 | #ifndef PRODUCT |
185 | // TODO(25041): When reading, this class copies out the filenames and contents |
186 | // into new buffers. It does this because the lifetime of |bytes| is uncertain. |
187 | // If |bytes| is pinned in memory, then we could instead load up |
188 | // |filenames_| and |contents_| with pointers into |bytes| without making |
189 | // copies. |
190 | class TarArchive { |
191 | public: |
192 | TarArchive(uint8_t* bytes, intptr_t bytes_length) |
193 | : rs_(bytes, bytes_length) {} |
194 | |
195 | void Read() { |
196 | while (HasNext()) { |
197 | char* filename; |
198 | uint8_t* data; |
199 | intptr_t data_length; |
200 | if (Next(&filename, &data, &data_length)) { |
201 | filenames_.Add(filename); |
202 | contents_.Add(data); |
203 | content_lengths_.Add(data_length); |
204 | } |
205 | } |
206 | } |
207 | |
208 | char* NextFilename() { return filenames_.RemoveLast(); } |
209 | |
210 | uint8_t* NextContent() { return contents_.RemoveLast(); } |
211 | |
212 | intptr_t NextContentLength() { return content_lengths_.RemoveLast(); } |
213 | |
214 | bool HasMore() const { return filenames_.length() > 0; } |
215 | |
216 | intptr_t Length() const { return filenames_.length(); } |
217 | |
218 | private: |
219 | enum { |
220 | = 0, |
221 | = 100, |
222 | = 124, |
223 | = 12, |
224 | = 156, |
225 | = 1, |
226 | = 512, |
227 | }; |
228 | |
229 | enum TarType { |
230 | kTarAregType = '\0', |
231 | kTarRegType = '0', |
232 | kTarLnkType = '1', |
233 | kTarSymType = '2', |
234 | kTarChrType = '3', |
235 | kTarBlkType = '4', |
236 | kTarDirType = '5', |
237 | kTarFifoType = '6', |
238 | kTarContType = '7', |
239 | kTarXhdType = 'x', |
240 | kTarXglType = 'g', |
241 | }; |
242 | |
243 | bool HasNext() const { return !EndOfArchive(); } |
244 | |
245 | bool Next(char** filename, uint8_t** data, intptr_t* data_length) { |
246 | intptr_t startOfBlock = rs_.Position(); |
247 | *filename = ReadFilename(); |
248 | rs_.SetPosition(startOfBlock + kTarHeaderSizeOffset); |
249 | intptr_t size = ReadSize(); |
250 | rs_.SetPosition(startOfBlock + kTarHeaderTypeOffset); |
251 | TarType type = ReadType(); |
252 | SeekToNextBlock(kTarHeaderSize); |
253 | if ((type != kTarRegType) && (type != kTarAregType)) { |
254 | SkipContents(size); |
255 | return false; |
256 | } |
257 | ReadContents(data, size); |
258 | *data_length = size; |
259 | return true; |
260 | } |
261 | |
262 | void SeekToNextBlock(intptr_t blockSize) { |
263 | intptr_t remainder = blockSize - (rs_.Position() % blockSize); |
264 | rs_.Advance(remainder); |
265 | } |
266 | |
267 | uint8_t PeekByte(intptr_t i) const { |
268 | return *(rs_.AddressOfCurrentPosition() + i); |
269 | } |
270 | |
271 | bool EndOfArchive() const { |
272 | if (rs_.PendingBytes() < (kTarHeaderSize * 2)) { |
273 | return true; |
274 | } |
275 | for (intptr_t i = 0; i < (kTarHeaderSize * 2); i++) { |
276 | if (PeekByte(i) != 0) { |
277 | return false; |
278 | } |
279 | } |
280 | return true; |
281 | } |
282 | |
283 | TarType ReadType() { |
284 | return static_cast<TarType>(ReadStream::Raw<1, uint8_t>::Read(&rs_)); |
285 | } |
286 | |
287 | void SkipContents(intptr_t size) { |
288 | rs_.Advance(size); |
289 | SeekToNextBlock(kTarHeaderSize); |
290 | } |
291 | |
292 | intptr_t ReadCString(char** s, intptr_t length) { |
293 | intptr_t to_read = Utils::Minimum(length, rs_.PendingBytes()); |
294 | char* result = new char[to_read + 1]; |
295 | strncpy(result, |
296 | reinterpret_cast<const char*>(rs_.AddressOfCurrentPosition()), |
297 | to_read); |
298 | result[to_read] = '\0'; |
299 | rs_.SetPosition(rs_.Position() + to_read); |
300 | *s = result; |
301 | return to_read; |
302 | } |
303 | |
304 | intptr_t ReadSize() { |
305 | char* octalSize; |
306 | unsigned int size; |
307 | |
308 | ReadCString(&octalSize, kTarHeaderSizeSize); |
309 | int result = sscanf(octalSize, "%o" , &size); |
310 | delete[] octalSize; |
311 | |
312 | if (result != 1) { |
313 | return 0; |
314 | } |
315 | return size; |
316 | } |
317 | |
318 | char* ReadFilename() { |
319 | char* result; |
320 | intptr_t result_length = ReadCString(&result, kTarHeaderFilenameSize); |
321 | if (result[0] == '/') { |
322 | return result; |
323 | } |
324 | char* fixed_result = new char[result_length + 2]; // '/' + '\0'. |
325 | fixed_result[0] = '/'; |
326 | strncpy(&fixed_result[1], result, result_length); |
327 | fixed_result[result_length + 1] = '\0'; |
328 | delete[] result; |
329 | return fixed_result; |
330 | } |
331 | |
332 | void ReadContents(uint8_t** data, intptr_t size) { |
333 | uint8_t* result = new uint8_t[size]; |
334 | rs_.ReadBytes(result, size); |
335 | SeekToNextBlock(kTarHeaderSize); |
336 | *data = result; |
337 | } |
338 | |
339 | ReadStream rs_; |
340 | GrowableArray<char*> filenames_; |
341 | GrowableArray<uint8_t*> contents_; |
342 | GrowableArray<intptr_t> content_lengths_; |
343 | |
344 | DISALLOW_COPY_AND_ASSIGN(TarArchive); |
345 | }; |
346 | |
347 | static void ContentsFinalizer(void* isolate_callback_data, |
348 | Dart_WeakPersistentHandle handle, |
349 | void* peer) { |
350 | uint8_t* data = reinterpret_cast<uint8_t*>(peer); |
351 | delete[] data; |
352 | } |
353 | |
354 | static void FilenameFinalizer(void* isolate_callback_data, |
355 | Dart_WeakPersistentHandle handle, |
356 | void* peer) { |
357 | char* filename = reinterpret_cast<char*>(peer); |
358 | delete[] filename; |
359 | } |
360 | |
361 | #endif |
362 | |
363 | DEFINE_NATIVE_ENTRY(VMService_DecodeAssets, 0, 1) { |
364 | #ifndef PRODUCT |
365 | GET_NON_NULL_NATIVE_ARGUMENT(TypedData, data, arguments->NativeArgAt(0)); |
366 | Api::Scope scope(thread); |
367 | Dart_Handle data_handle = Api::NewHandle(thread, data.raw()); |
368 | Dart_Handle result_list; |
369 | { |
370 | TransitionVMToNative transition(thread); |
371 | |
372 | Dart_TypedData_Type typ; |
373 | void* bytes; |
374 | intptr_t length; |
375 | Dart_Handle err = |
376 | Dart_TypedDataAcquireData(data_handle, &typ, &bytes, &length); |
377 | ASSERT(!Dart_IsError(err)); |
378 | |
379 | TarArchive archive(reinterpret_cast<uint8_t*>(bytes), length); |
380 | archive.Read(); |
381 | |
382 | err = Dart_TypedDataReleaseData(data_handle); |
383 | ASSERT(!Dart_IsError(err)); |
384 | |
385 | intptr_t archive_size = archive.Length(); |
386 | |
387 | result_list = Dart_NewList(2 * archive_size); |
388 | ASSERT(!Dart_IsError(result_list)); |
389 | |
390 | intptr_t idx = 0; |
391 | while (archive.HasMore()) { |
392 | char* filename = archive.NextFilename(); |
393 | intptr_t filename_length = strlen(filename); |
394 | uint8_t* contents = archive.NextContent(); |
395 | intptr_t contents_length = archive.NextContentLength(); |
396 | |
397 | Dart_Handle dart_filename = Dart_NewExternalLatin1String( |
398 | reinterpret_cast<uint8_t*>(filename), filename_length, filename, |
399 | filename_length, FilenameFinalizer); |
400 | ASSERT(!Dart_IsError(dart_filename)); |
401 | |
402 | Dart_Handle dart_contents = Dart_NewExternalTypedDataWithFinalizer( |
403 | Dart_TypedData_kUint8, contents, contents_length, contents, |
404 | contents_length, ContentsFinalizer); |
405 | ASSERT(!Dart_IsError(dart_contents)); |
406 | |
407 | Dart_ListSetAt(result_list, idx, dart_filename); |
408 | Dart_ListSetAt(result_list, (idx + 1), dart_contents); |
409 | idx += 2; |
410 | } |
411 | } |
412 | return Api::UnwrapArrayHandle(thread->zone(), result_list).raw(); |
413 | #else |
414 | return Object::null(); |
415 | #endif |
416 | } |
417 | |
418 | } // namespace dart |
419 | |