OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import <Foundation/Foundation.h> | 5 #import <Foundation/Foundation.h> |
6 #include <asl.h> | 6 #include <asl.h> |
7 #include <libgen.h> | 7 #include <libgen.h> |
8 #include <stdarg.h> | 8 #include <stdarg.h> |
9 #include <stdio.h> | 9 #include <stdio.h> |
10 | 10 |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
121 } | 121 } |
122 | 122 |
123 } // namespace | 123 } // namespace |
124 | 124 |
125 // A delegate that is called when the simulated app is started or ended in the | 125 // A delegate that is called when the simulated app is started or ended in the |
126 // simulator. | 126 // simulator. |
127 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> { | 127 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> { |
128 @private | 128 @private |
129 NSString* stdioPath_; | 129 NSString* stdioPath_; |
130 NSString* developerDir_; | 130 NSString* developerDir_; |
131 NSString* simulatorHome_; | |
131 NSThread* outputThread_; | 132 NSThread* outputThread_; |
132 NSBundle* simulatorBundle_; | 133 NSBundle* simulatorBundle_; |
133 BOOL appRunning_; | 134 BOOL appRunning_; |
134 } | 135 } |
135 @end | 136 @end |
136 | 137 |
137 // An implementation that copies the simulated app's stdio to stdout of this | 138 // An implementation that copies the simulated app's stdio to stdout of this |
138 // executable. While it would be nice to get stdout and stderr independently | 139 // executable. While it would be nice to get stdout and stderr independently |
139 // from iOS Simulator, issues like I/O buffering and interleaved output | 140 // from iOS Simulator, issues like I/O buffering and interleaved output |
140 // between iOS Simulator and the app would cause iossim to display things out | 141 // between iOS Simulator and the app would cause iossim to display things out |
141 // of order here. Printing all output to a single file keeps the order correct. | 142 // of order here. Printing all output to a single file keeps the order correct. |
142 // Instances of this classe should be initialized with the location of the | 143 // Instances of this classe should be initialized with the location of the |
143 // simulated app's output file. When the simulated app starts, a thread is | 144 // simulated app's output file. When the simulated app starts, a thread is |
144 // started which handles copying data from the simulated app's output file to | 145 // started which handles copying data from the simulated app's output file to |
145 // the stdout of this executable. | 146 // the stdout of this executable. |
146 @implementation SimulatorDelegate | 147 @implementation SimulatorDelegate |
147 | 148 |
148 // Specifies the file locations of the simulated app's stdout and stderr. | 149 // Specifies the file locations of the simulated app's stdout and stderr. |
149 - (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath | 150 - (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath |
150 developerDir:(NSString*)developerDir { | 151 developerDir:(NSString*)developerDir |
152 simulatorHome:(NSString*)simulatorHome { | |
151 self = [super init]; | 153 self = [super init]; |
152 if (self) { | 154 if (self) { |
153 stdioPath_ = [stdioPath copy]; | 155 stdioPath_ = [stdioPath copy]; |
154 developerDir_ = [developerDir copy]; | 156 developerDir_ = [developerDir copy]; |
157 simulatorHome_ = [simulatorHome copy]; | |
155 } | 158 } |
156 | 159 |
157 return self; | 160 return self; |
158 } | 161 } |
159 | 162 |
160 - (void)dealloc { | 163 - (void)dealloc { |
161 [stdioPath_ release]; | 164 [stdioPath_ release]; |
162 [developerDir_ release]; | 165 [developerDir_ release]; |
163 [simulatorBundle_ release]; | 166 [simulatorBundle_ release]; |
164 [super dealloc]; | 167 [super dealloc]; |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
273 localizedDescription, [error domain], | 276 localizedDescription, [error domain], |
274 static_cast<long int>([error code])); | 277 static_cast<long int>([error code])); |
275 } else { | 278 } else { |
276 LogError(@"Simulator ended with error: \"%@\" (%@:%ld)", | 279 LogError(@"Simulator ended with error: \"%@\" (%@:%ld)", |
277 localizedDescription, [error domain], | 280 localizedDescription, [error domain], |
278 static_cast<long int>([error code])); | 281 static_cast<long int>([error code])); |
279 exit(kExitFailure); | 282 exit(kExitFailure); |
280 } | 283 } |
281 } | 284 } |
282 | 285 |
283 // Check if the simulated app exited abnormally by looking for system log | 286 // Try to determine if the simulated app crashed or quit with a non-zero |
284 // messages from launchd that refer to the simulated app's PID. Limit query | 287 // status code. iOS Simluator handles things a bit differently depending on |
285 // to messages in the last minute since PIDs are cyclical. | 288 // the version, so first determine the iOS version being used. |
286 aslmsg query = asl_new(ASL_TYPE_QUERY); | 289 BOOL badEntryFound = NO; |
287 asl_set_query(query, ASL_KEY_SENDER, "launchd", | 290 NSString* versionString = |
288 ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING); | 291 [[[session sessionConfig] simulatedSystemRoot] sdkVersion]; |
289 asl_set_query(query, ASL_KEY_REF_PID, | 292 NSInteger majorVersion = [[versionString substringToIndex: |
290 [[[session simulatedApplicationPID] stringValue] UTF8String], | 293 [versionString rangeOfString:@"."].location] intValue]; |
TVL
2013/09/12 13:56:08
[[[versionString componentsSeparatedByString:@"."]
lliabraa
2013/09/12 16:11:55
hmm..my compiler didn't like firstObject (and I do
TVL
2013/09/12 16:36:44
Ah, you must be using a mac sdk without it protoyp
| |
291 ASL_QUERY_OP_EQUAL); | 294 if (majorVersion <= 6) { |
292 asl_set_query(query, ASL_KEY_TIME, "-1m", ASL_QUERY_OP_GREATER_EQUAL); | 295 // In iOS 6 and before, logging from the simulated apps went to the main |
296 // system logs, so use ASL to check if the simulated app exited abnormally | |
297 // by looking for system log messages from launchd that refer to the | |
298 // simulated app's PID. Limit query to messages in the last minute since | |
299 // PIDs are cyclical. | |
300 aslmsg query = asl_new(ASL_TYPE_QUERY); | |
301 asl_set_query(query, ASL_KEY_SENDER, "launchd", | |
302 ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING); | |
303 asl_set_query(query, ASL_KEY_REF_PID, | |
304 [[[session simulatedApplicationPID] stringValue] UTF8String], | |
305 ASL_QUERY_OP_EQUAL); | |
306 asl_set_query(query, ASL_KEY_TIME, "-1m", ASL_QUERY_OP_GREATER_EQUAL); | |
293 | 307 |
294 // Log any messages found, and take note of any messages that may indicate the | 308 // Log any messages found, and take note of any messages that may indicate |
295 // app crashed or did not exit cleanly. | 309 // the app crashed or did not exit cleanly. |
296 aslresponse response = asl_search(NULL, query); | 310 aslresponse response = asl_search(NULL, query); |
297 BOOL badEntryFound = NO; | 311 aslmsg entry; |
298 aslmsg entry; | 312 while ((entry = aslresponse_next(response)) != NULL) { |
299 while ((entry = aslresponse_next(response)) != NULL) { | 313 const char* message = asl_get(entry, ASL_KEY_MSG); |
300 const char* message = asl_get(entry, ASL_KEY_MSG); | 314 LogWarning(@"Console message: %s", message); |
301 LogWarning(@"Console message: %s", message); | 315 // Some messages are harmless, so don't trigger a failure for them. |
302 // Some messages are harmless, so don't trigger a failure for them. | 316 if (strstr(message, "The following job tried to hijack the service")) |
303 if (strstr(message, "The following job tried to hijack the service")) | 317 continue; |
304 continue; | 318 badEntryFound = YES; |
305 badEntryFound = YES; | 319 } |
320 } else { | |
321 // Otherwise, the iOS Simulator's system logging is sandboxed, so parse the | |
322 // sandboxed system.log file for known errors. | |
323 NSString* relativePathToSystemLog = | |
324 [NSString stringWithFormat: | |
325 @"Library/Logs/iOS Simulator/%@/system.log", versionString]; | |
326 NSString* path = | |
327 [simulatorHome_ stringByAppendingPathComponent:relativePathToSystemLog]; | |
328 NSString* content = [NSString stringWithContentsOfFile:path | |
329 encoding:NSUTF8StringEncoding | |
330 error:NULL]; | |
331 NSArray* lines = [content componentsSeparatedByCharactersInSet: | |
332 [NSCharacterSet newlineCharacterSet]]; | |
333 for (NSString* line in lines) { | |
TVL
2013/09/12 13:56:08
You have no checks here for if the file doesn't ex
lliabraa
2013/09/12 16:11:55
Done.
| |
334 NSString* const kErrorString = @"Service exited with abnormal code:"; | |
335 if ([line rangeOfString:kErrorString].location != NSNotFound) { | |
336 LogWarning(@"Console message: %@", line); | |
337 badEntryFound = YES; | |
338 break; | |
339 } | |
340 } | |
341 // Remove the log file so subsequent invocations of iossim won't be looking | |
342 // at stale logs. | |
343 remove([path UTF8String]); | |
TVL
2013/09/12 13:56:08
[path fileSystemRepresentation] (but we probably h
lliabraa
2013/09/12 16:11:55
Done.
| |
306 } | 344 } |
307 | 345 |
308 // If the query returned any nasty-looking results, iossim should exit with | 346 // If the query returned any nasty-looking results, iossim should exit with |
309 // non-zero status. | 347 // non-zero status. |
310 if (badEntryFound) { | 348 if (badEntryFound) { |
311 LogError(@"Simulated app crashed or exited with non-zero status"); | 349 LogError(@"Simulated app crashed or exited with non-zero status"); |
312 exit(kExitAppCrashed); | 350 exit(kExitAppCrashed); |
313 } | 351 } |
314 exit(kExitSuccess); | 352 exit(kExitSuccess); |
315 } | 353 } |
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
693 // Create the config and simulator session. | 731 // Create the config and simulator session. |
694 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, | 732 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, |
695 systemRoot, | 733 systemRoot, |
696 stdioPath, | 734 stdioPath, |
697 stdioPath, | 735 stdioPath, |
698 appArgs, | 736 appArgs, |
699 appEnv, | 737 appEnv, |
700 deviceFamily); | 738 deviceFamily); |
701 SimulatorDelegate* delegate = | 739 SimulatorDelegate* delegate = |
702 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath | 740 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath |
703 developerDir:developerDir] autorelease]; | 741 developerDir:developerDir |
742 simulatorHome:simHomePath] autorelease]; | |
704 DTiPhoneSimulatorSession* session = BuildSession(delegate); | 743 DTiPhoneSimulatorSession* session = BuildSession(delegate); |
705 | 744 |
706 // Start the simulator session. | 745 // Start the simulator session. |
707 NSError* error; | 746 NSError* error; |
708 BOOL started = [session requestStartWithConfig:config | 747 BOOL started = [session requestStartWithConfig:config |
709 timeout:sessionStartTimeout | 748 timeout:sessionStartTimeout |
710 error:&error]; | 749 error:&error]; |
711 | 750 |
712 // Spin the runtime indefinitely. When the delegate gets the message that the | 751 // Spin the runtime indefinitely. When the delegate gets the message that the |
713 // app has quit it will exit this program. | 752 // app has quit it will exit this program. |
714 if (started) { | 753 if (started) { |
715 [[NSRunLoop mainRunLoop] run]; | 754 [[NSRunLoop mainRunLoop] run]; |
716 } else { | 755 } else { |
717 LogError(@"Simulator failed request to start: \"%@\" (%@:%ld)", | 756 LogError(@"Simulator failed request to start: \"%@\" (%@:%ld)", |
718 [error localizedDescription], | 757 [error localizedDescription], |
719 [error domain], static_cast<long int>([error code])); | 758 [error domain], static_cast<long int>([error code])); |
720 } | 759 } |
721 | 760 |
722 // Note that this code is only executed if the simulator fails to start | 761 // Note that this code is only executed if the simulator fails to start |
723 // because once the main run loop is started, only the delegate calling | 762 // because once the main run loop is started, only the delegate calling |
724 // exit() will end the program. | 763 // exit() will end the program. |
725 [pool drain]; | 764 [pool drain]; |
726 return kExitFailure; | 765 return kExitFailure; |
727 } | 766 } |
OLD | NEW |