How to determine if after launch the application crashed the last time it left?

One of the disaster recovery frameworks that I found looks like this: if there are crash reports in the ~ / Library / Logs / CrashReporter folder for the application, it determines that the crash happened earlier. It then allows the user to send the developer a crash log. And finally, he removes these logs.

This is the removal that bothers me. This is not acceptable to me. The user may want to see these logs later. It is simply rude that the user simply deletes their crash logs.

So I'm looking for a better way to spot a crash.

It actually doesnโ€™t work to store the latest logs with read errors in user defaults or user data, as this means that if the user deletes the data and the default values โ€‹โ€‹(which he has the right to do whenever he or she wishes), and then launches the application, it will be detected as broken the last time it stopped. So it does not work.

Any ideas?

+7
objective-c cocoa macos
source share
3 answers

Write to the file every time you exit. Read this file every time you open the program. Write a specific integer to the file each time the program exits, which is read every time the program starts.

For example, if 0 is written to a file, which may indicate non-crash. Everything else means crash.

+2
source share

I solved it this way (the registry should be available for every application launch, though):

@implementation ICNRegistry /** * @public * @date edited 2014-04-28 */ + (ICNRegistry*) registry { static ICNRegistry* registrySingleton = nil; @synchronized(self) { if (!registrySingleton) { registrySingleton = [[ICNRegistry alloc] initPrivate]; } return registrySingleton; } } #pragma mark - #pragma mark Initialization /** * @private * @date edited 2014-05-09 */ - (id) initPrivate { self = [super init]; if (self) { _appQuitSuccessfulIndicatorFilePath = [[self cacheRootForClass:[self class]] stringByAppendingPathComponent: @"didQuitSuccessfullyIndicator"]; // (implementation of cacheRootForClass not included) // Set _lastAppQuitWasSuccessful: [self readPreviousAppSuccessStatusAndStartMonitoringCurrentLaunchQuit]; // Start monitoring app quit: [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(registerAppWillQuitSuccessfully) name: UIApplicationWillTerminateNotification object:nil]; } return self; } #pragma mark - #pragma mark Monitoring App Quits /** * @private * Checks if a file exists at a specific location. If the file exists, then * last app quit was successful and lastAppQuitWasSuccessful will be set to YES. * Otherwise, if the file does not exist, lastAppQuitWasSuccessful will be set * to NO. * * @discussion * When app quits in a normal way, a file will be created on app termination, that * way indicating successful quit. If the app quits unexpectedly, then the file * will not be written and thus the quit will be indicated as not being successful. * * @since 2014-05-09 * @date edited 2014-05-09 */ - (void) readPreviousAppSuccessStatusAndStartMonitoringCurrentLaunchQuit { NSFileManager* fm = [NSFileManager defaultManager]; self.lastAppQuitWasSuccessful = NO; if ([fm fileExistsAtPath:self.appQuitSuccessfulIndicatorFilePath]) { self.lastAppQuitWasSuccessful = YES; NSError* error; [fm removeItemAtPath:self.appQuitSuccessfulIndicatorFilePath error:&error]; if (error) { ICN_WARN(@"failed to delete 'app quit successful' indicator file at " @"path %@; %@", self.appQuitSuccessfulIndicatorFilePath, error.localizedDescription); } } } /** * @private * @since 2014-05-09 * @date edited 2014-05-09 */ - (void) registerAppWillQuitSuccessfully { NSFileManager* fm = [NSFileManager defaultManager]; // Create file path if not present: NSError* error; NSString* dir = [self.appQuitSuccessfulIndicatorFilePath stringByDeletingLastPathComponent]; [fm createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&error]; if (error) { ICN_WARN(@"failed to create dir %@; %@", dir, error.localizedDescription); return; } [fm createFileAtPath:self.appQuitSuccessfulIndicatorFilePath contents:nil attributes:nil]; } #pragma mark - @end 

Then, to illustrate, in my application, I do something like this:

 - (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { // If app did quit unexpectedly (due to crash, reset, etc) last time it was // used, then clear all cache to avoid inconsistencies if (![[ICNRegistry registry] lastAppQuitWasSuccessful]) { [ICNUIViewImageCache clearCache]; } } 

It should be noted that with this solution, the FIRST time the application starts, it will be detected as if it had worked earlier. If this is a problem, then a file indicator may be created the first time the application is launched.

+1
source share

This is an old post, but I was looking for a good way to do this and did not stumble upon it.

Perhaps this will help someone in a similar situation.

I do this on Windows, but maybe it is also possible for osx / linux using the named pthread clutches, although I donโ€™t know the policy on how such mutexes are handled if the application is completed, which is crucial for this technique.

The problem with using one flag is that it does not work in a situation where there can be several instances of the application:

  • Application 1 starts. Checks the "Do not close the flag correctly" box.
  • Application 2 is running. He sees that it is not closed, because the flag is set and thinks that a failure occurred when it was not there.

This is a little difficult to do, and there are probably several ways to do this, but this method still works (at least on Windows):

Create a global mutez with the new GUID / UUID as the name.
The GUID is different for each instance of the application, so each of them will have a unique named mutex. The GUID is written to a file containing a list of GUIDs. If the application disconnects correctly, you remove the GUID from the file and the mutex closes. If the application crashes or is terminated, the GUID is not deleted from the file, but the mutex is destroyed by the OS without an application that should do this.

When you launch the application, you can run the GUID in the list and call OpenMutex on them and mutexes that do not exist (GetLastError returns ERROR_FILE_NOT_FOUND). Any non-existent mutexes indicate that a failure / termination has occurred. All GUIDs that have this property can be removed from the list at this time.

Another thing you can do is remove the bad GUIDs from the list when the job completes correctly. This turns into the following situation:

  • Appendix 1 is starting
  • Launch Application 2
  • Crash Application 1
  • Appendix 2 completed successfully
  • Application 3 starts.

When application 3 is running, it should not publish that a failure has occurred because the last shutdown was good.

I also used a kind of spinlock to gain access to a file containing GUIDs that will execute after a period of time and will not add manual for the file to the file. In such cases, it is probably best to run the application anyway and lose the crash detection rather than not running the application at all.

Other caveats should keep the file locked while requesting and updating the file to avoid race conditions. In windows, this prevents the use of stl filestreams, since you will need to trim the file after reading it, while maintaining exclusive access to the file.

0
source share

All Articles