From 7c1f15e7db6917c91db38325f8de0f4b11f71c55 Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 23 Aug 2018 15:33:37 +0800 Subject: [PATCH] [WEEX-587][iOS] excpetion report fix (#1469) * [iOS] fix empty check logic * [iOS] report detail info of bundel (pageNmae,stagelist,errorHistory,template) * [iOS] add apm switch --- .../WeexSDK/Sources/Bridge/WXBridgeContext.m | 23 --- ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h | 3 +- ios/sdk/WeexSDK/Sources/Engine/WXSDKError.m | 3 +- .../WeexSDK/Sources/Manager/WXBridgeManager.m | 5 + .../Sources/Manager/WXComponentManager.mm | 1 + ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h | 5 + ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m | 7 + .../Sources/Monitor/WXExceptionUtils.m | 43 +++++- .../Sources/Performance/WXApmForInstance.h | 12 +- .../Sources/Performance/WXInstanceApm.m | 143 +++++++++++++++--- 10 files changed, 196 insertions(+), 49 deletions(-) diff --git a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m index 7497dabe5a..bf747fed0d 100644 --- a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m +++ b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m @@ -1046,9 +1046,6 @@ + (void)mountContextEnvironment:(JSContext*)context message = [NSString stringWithFormat:@"[WX_KEY_EXCEPTION_WXBRIDGE] [%@:%@:%@] %@\n%@\n%@\n%@\n%@\n%@", exception[@"sourceURL"], exception[@"line"], exception[@"column"], [exception toString], [exception[@"stack"] toObject], instance.scriptURL.absoluteString, instance.callCreateInstanceContext?:@"", instance.createInstanceContextResult?:@"", instance.executeRaxApiResult?:@""]; userInfo = @{@"jsMainBundleStringContentLength":instance.userInfo[@"jsMainBundleStringContentLength"]?:@"", @"jsMainBundleStringContentMd5":instance.userInfo[@"jsMainBundleStringContentMd5"]?:@""}; - if ([self checkEmptyScreen:instance]) { - errorCode = [NSString stringWithFormat:@"%d", WX_KEY_EXCEPTION_EMPTY_SCREEN_JS]; - } } if (commitException) { @@ -1121,24 +1118,4 @@ + (void)handleConsoleOutputWithArgument:(NSArray*)arguments logLevel:(WXLogFlag) } }]; } - -+ (BOOL) checkEmptyScreen:(WXSDKInstance *) instance -{ - if(!instance){ - return false; - } - if (!(instance.rootView) ) { - return true; - } - CGRect rootFrame = instance.rootView.frame; - - if (rootFrame.size.height <=0 || rootFrame.size.width <=0) { - return true; - } - - if (!(instance.rootView.subviews) || instance.rootView.subviews.count <=0) { - return true; - } - return false; -} @end diff --git a/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h b/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h index ba446b2521..e7606669be 100644 --- a/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h +++ b/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.h @@ -79,7 +79,8 @@ typedef NS_ENUM(int, WXSDKErrCode) WX_KEY_EXCEPTION_ABILITY_DOWN_TOH5 = -9602, WX_KEY_EXCEPTION_ABILITY_DOWN_ = -9603, - WX_KEY_EXCEPTION_EMPTY_SCREEN_JS = -9700 + WX_KEY_EXCEPTION_EMPTY_SCREEN_JS = -9700, + WX_KEY_EXCEPTION_EMPTY_SCREEN_NATIVE = -9701 }; typedef NS_ENUM (NSInteger,WXSDKErrorType) diff --git a/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.m b/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.m index c3b9a7791a..48c046f107 100644 --- a/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.m +++ b/ios/sdk/WeexSDK/Sources/Engine/WXSDKError.m @@ -99,7 +99,8 @@ +(NSDictionary *) getMap @(WX_KEY_EXCEPTION_ABILITY_DOWN_TOH5):@{ERROR_TYPE:@(WX_NATIVE_ERROR),ERROR_GROUP:@(WX_NATIVE)}, @(WX_ERR_JSFRAMEWORK_START):@{ERROR_TYPE:@(WX_NATIVE_ERROR),ERROR_GROUP:@(WX_NATIVE)}, @(WX_KEY_EXCEPTION_ABILITY_DOWN_):@{ERROR_TYPE:@(WX_NATIVE_ERROR),ERROR_GROUP:@(WX_NATIVE)}, - @(WX_KEY_EXCEPTION_EMPTY_SCREEN_JS):@{ERROR_TYPE:@(WX_RENDER_ERROR),ERROR_GROUP:@(WX_JS)} + @(WX_KEY_EXCEPTION_EMPTY_SCREEN_JS):@{ERROR_TYPE:@(WX_RENDER_ERROR),ERROR_GROUP:@(WX_JS)}, + @(WX_KEY_EXCEPTION_EMPTY_SCREEN_NATIVE):@{ERROR_TYPE:@(WX_RENDER_ERROR),ERROR_GROUP:@(WX_NATIVE)} }; }); return codeMap; diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m index f1a4296e7a..c74f79df93 100644 --- a/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m +++ b/ios/sdk/WeexSDK/Sources/Manager/WXBridgeManager.m @@ -156,6 +156,11 @@ - (void)createInstance:(NSString *)instance [self.instanceIdStack insertObject:instance atIndex:0]; } } + //third team impl... + WXSDKInstance* sdkInstance = [WXSDKManager instanceForID:instance]; + if (sdkInstance) { + sdkInstance.apmInstance.isStartRender = YES; + } __weak typeof(self) weakSelf = self; WXPerformBlockOnBridgeThread(^(){ [WXTracingManager startTracingWithInstanceId:instance ref:nil className:nil name:WXTExecJS phase:WXTracingBegin functionName:@"createInstance" options:@{@"threadName":WXTJSBridgeThread}]; diff --git a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm index a9146cba73..af710b73cb 100644 --- a/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm +++ b/ios/sdk/WeexSDK/Sources/Manager/WXComponentManager.mm @@ -267,6 +267,7 @@ - (void)addComponent:(NSDictionary *)componentData toSupercomponent:(NSString *) WXLogWarning(@"addComponent,superRef from js never exit ! check JS action, supRef:%@",superRef); return; } + supercomponent.weexInstance.apmInstance.hasAddView = YES; [self _recursivelyAddComponent:componentData toSupercomponent:supercomponent atIndex:index appendingInTree:appendingInTree]; } diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h index 56e6a6b934..95fa7f24ff 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h +++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.h @@ -316,6 +316,11 @@ typedef NS_ENUM(NSInteger, WXErrorCode) {//error.code */ - (NSURL *)completeURL:(NSString *)url; +/** + * jsbundle str ,may be nil (weak) + */ +- (NSString*) bundleTemplate; + /** * application performance statistics */ diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m index 0b9782530f..69ee169722 100644 --- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m +++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m @@ -218,6 +218,7 @@ - (void)renderWithURL:(NSURL *)url options:(NSDictionary *)options data:(id)data return; } [self.apmInstance startRecord:self.instanceId]; + self.apmInstance.isStartRender = YES; self.needValidate = [[WXHandlerFactory handlerForProtocol:@protocol(WXValidateProtocol)] needValidate:url]; WXResourceRequest *request = [WXResourceRequest requestWithURL:url resourceType:WXResourceTypeMainBundle referrer:@"" cachePolicy:NSURLRequestUseProtocolCachePolicy]; @@ -239,6 +240,11 @@ - (void)renderView:(NSString *)source options:(NSDictionary *)options data:(id)d [WXTracingManager setBundleJSType:source instanceId:self.instanceId]; } +- (NSString*) bundleTemplate +{ + return self.mainBundleString; +} + - (void)_renderWithMainBundleString:(NSString *)mainBundleString { if (!self.instanceId) { @@ -248,6 +254,7 @@ - (void)_renderWithMainBundleString:(NSString *)mainBundleString //some case , with out render (url) [self.apmInstance startRecord:self.instanceId]; + self.apmInstance.isStartRender = YES; self.performance.renderTimeOrigin = CACurrentMediaTime()*1000; [self.apmInstance onStage:KEY_PAGE_STAGES_RENDER_ORGIGIN]; diff --git a/ios/sdk/WeexSDK/Sources/Monitor/WXExceptionUtils.m b/ios/sdk/WeexSDK/Sources/Monitor/WXExceptionUtils.m index 9599505ff4..286b6550b3 100644 --- a/ios/sdk/WeexSDK/Sources/Monitor/WXExceptionUtils.m +++ b/ios/sdk/WeexSDK/Sources/Monitor/WXExceptionUtils.m @@ -35,22 +35,38 @@ + (void)commitCriticalExceptionRT:(NSString *)instanceId errCode:(NSString *)err NSString *bundleUrlCommit = @"BundleUrlDefault"; NSString *instanceIdCommit = @"InstanceIdDefalut"; + WXSDKInstance * instance ; if(![WXUtility isBlankString:instanceId]){ instanceIdCommit = instanceId; - WXSDKInstance * instance = [WXSDKManager instanceForID:instanceId]; + instance = [WXSDKManager instanceForID:instanceId]; if(instance){ bundleUrlCommit = instance.pageName?:([instance.scriptURL absoluteString]?:bundleUrlCommit); + NSMutableDictionary* extInfo = [[NSMutableDictionary alloc] initWithDictionary:extParams]; if (instance.containerInfo && instance.containerInfo.count >0) { - NSMutableDictionary* extInfo = [[NSMutableDictionary alloc] initWithDictionary:extParams]; [extInfo addEntriesFromDictionary:instance.containerInfo]; - extParams = extInfo; } + [extInfo setObject:[self _convertInstanceStageToStr:instance] forKey:@"wxStageList"]; + [extInfo setObject:instance.pageName?:@"unKnowPageNameCaseUnSet" forKey:@"wxPageName"]; + [extInfo setObject:instance.bundleTemplate?:@"has recycle" forKey:@"wxTemplateOfBundle"]; + extParams = extInfo; }else if([instanceIdCommit hasPrefix:@"WX_KEY_EXCEPTION"]){ bundleUrlCommit = instanceId; } } - + WXJSExceptionInfo * jsExceptionInfo = [[WXJSExceptionInfo alloc] initWithInstanceId:instanceIdCommit bundleUrl:bundleUrlCommit errorCode:errCode functionName:function exception:exception userInfo: [extParams mutableCopy]]; + + //record top5 erromsg ,if errorType is not WX_RENDER_ERROR + NSNumberFormatter *formater = [[NSNumberFormatter alloc] init]; + formater.numberStyle = NSNumberFormatterDecimalStyle; + NSNumber *codeNumber = [formater numberFromString:errCode]; + if (codeNumber) { + WXSDKErrorType type = [WXSDKErrCodeUtil getErrorTypeByCode:codeNumber.intValue]; + if (type != WX_RENDER_ERROR && nil != instance) { + [instance.apmInstance recordErrorMsg:jsExceptionInfo]; + } + } + [WXExceptionUtils commitCriticalExceptionRT:jsExceptionInfo]; } @@ -64,4 +80,23 @@ + (void)commitCriticalExceptionRT:(WXJSExceptionInfo *)jsExceptionInfo{ } } ++ (NSString*) _convertInstanceStageToStr:(WXSDKInstance *)instance +{ + if (nil == instance || !instance.apmInstance.isOpenApm) { + return @""; + } + NSDictionary* stageDic = instance.apmInstance.stageDic; + if (!stageDic || stageDic.count <=0 ) { + return @"emptyStageRecord"; + } + NSString* stageStr = @""; + for (NSString* key in stageDic) { + NSNumber* time = [stageDic objectForKey:key]; + stageStr = [stageStr stringByAppendingFormat:@"%@ -> %@:%@",stageStr,key,time]; + } + return stageStr; +} + + + @end diff --git a/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.h b/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.h index 15748444a6..585add2dfb 100644 --- a/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.h +++ b/ios/sdk/WeexSDK/Sources/Performance/WXApmForInstance.h @@ -1,4 +1,5 @@ #import +#import "WXJSExceptionInfo.h" #pragma mark - const static string @@ -76,8 +77,12 @@ extern NSString* const VALUE_ERROR_CODE_DEFAULT; @interface WXApmForInstance : NSObject -@property (nonatomic, assign) bool isFSEnd; -@property (nonatomic, assign) bool isStartRecord; +@property (nonatomic, assign) BOOL isFSEnd; +@property (nonatomic,assign) BOOL isOpenApm; +@property (nonatomic, assign) BOOL isStartRecord; +@property (nonatomic, assign) BOOL hasAddView; +@property (nonatomic, assign) BOOL isDegrade; +@property (nonatomic, assign) BOOL isStartRender; #pragma mark - basic method @@ -106,4 +111,7 @@ extern NSString* const VALUE_ERROR_CODE_DEFAULT; - (void) actionImgLoad; - (void) actionImgLoadResult:(bool)succeed withErrorCode:(NSString*)errorCode; +#pragma mark record top5 errorMsg +- (void) recordErrorMsg:(WXJSExceptionInfo *)exception; +- (NSDictionary*) stageDic; @end diff --git a/ios/sdk/WeexSDK/Sources/Performance/WXInstanceApm.m b/ios/sdk/WeexSDK/Sources/Performance/WXInstanceApm.m index 3264a727ff..845464d91b 100644 --- a/ios/sdk/WeexSDK/Sources/Performance/WXInstanceApm.m +++ b/ios/sdk/WeexSDK/Sources/Performance/WXInstanceApm.m @@ -4,6 +4,11 @@ #import "WXSDKManager.h" #import "WXAppConfiguration.h" #import "WXUtility.h" +#import "WXComponentManager.h" +#import "WXConfigCenterProtocol.h" +#import "WXSDKEngine.h" +#import "WXSDKError.h" +#import "WXExceptionUtils.h" #pragma mark - const static string @@ -82,6 +87,9 @@ @interface WXApmForInstance () @property (nonatomic,strong) id apmProtocolInstance; @property (nonatomic,strong) NSString* instanceId; @property (nonatomic,strong) NSMutableDictionary* recordStatsMap; +@property (nonatomic,assign) BOOL isRecord; +@property (nonatomic,strong) NSMutableDictionary* recordStageMap; +@property (nonatomic,strong) NSMutableArray* errorList; @end @implementation WXApmForInstance @@ -91,9 +99,14 @@ - (instancetype) init { self = [super init]; if (self) { - id generater = [WXHandlerFactory handlerForProtocol:@protocol(WXApmGeneratorProtocol)]; - _apmProtocolInstance = [generater gengratorApmInstance:WEEX_PAGE_TOPIC]; _recordStatsMap = [[NSMutableDictionary alloc] init]; + _recordStageMap = [[NSMutableDictionary alloc] init]; + _errorList = [[NSMutableArray alloc] init]; + _isOpenApm = [self _loadApmSwitch]; + if (_isOpenApm) { + id generater = [WXHandlerFactory handlerForProtocol:@protocol(WXApmGeneratorProtocol)]; + _apmProtocolInstance = [generater gengratorApmInstance:WEEX_PAGE_TOPIC]; + } } return self; } @@ -111,7 +124,12 @@ - (void) onStage:(NSString *)name if (nil == _apmProtocolInstance) { return; } - [self.apmProtocolInstance onStage:name withValue:[WXUtility getUnixFixTimeMillis]]; + long time = [WXUtility getUnixFixTimeMillis]; + [self.apmProtocolInstance onStage:name withValue:time]; + __weak typeof(self) weakSelf = self; + WXPerformBlockOnComponentThread(^{ + [weakSelf.recordStageMap setObject:@(time) forKey:name]; + }); } - (void) setProperty:(NSString *)name withValue:(id)value @@ -134,10 +152,10 @@ - (void) setStatistic:(NSString *)name withValue:(double)value - (void) startRecord:(NSString*) instanceId { - if (nil == _apmProtocolInstance || self.isStartRecord) { + if (nil == _apmProtocolInstance || self.isRecord) { return; } - self.isStartRecord = YES; + self.isRecord = YES; _instanceId = instanceId; [self.apmProtocolInstance onStart:instanceId topic:WEEX_PAGE_TOPIC]; @@ -178,6 +196,7 @@ - (void) endRecord; [self onStage:KEY_PAGE_STAGES_DESTROY]; [self.apmProtocolInstance onEnd]; + [self _checkScreenEmptyAndReport]; } - (void) arriveFSRenderTime @@ -202,11 +221,14 @@ - (void) updateDiffStats:(NSString *)name withDiffValue:(double)diff if (nil == _apmProtocolInstance) { return; } - NSNumber* preNumber = [self.recordStatsMap objectForKey:name]; - double preVal = nil == preNumber?0:preNumber.doubleValue; - double currentVal = preVal + diff; - [self.recordStatsMap setObject:@(currentVal) forKey:name]; - [self setStatistic:name withValue:currentVal]; + __weak typeof(self) weakSelf = self; + WXPerformBlockOnComponentThread(^{ + NSNumber* preNumber = [self.recordStatsMap objectForKey:name]; + double preVal = nil == preNumber?0:preNumber.doubleValue; + double currentVal = preVal + diff; + [weakSelf.recordStatsMap setObject:@(currentVal) forKey:name]; + [weakSelf setStatistic:name withValue:currentVal]; + }); } - (void) updateMaxStats:(NSString *)name curMaxValue:(double)currentValue @@ -214,14 +236,17 @@ - (void) updateMaxStats:(NSString *)name curMaxValue:(double)currentValue if (nil == _apmProtocolInstance) { return; } - NSNumber* maxNumber = [self.recordStatsMap objectForKey:name]; - double maxVal = nil == maxNumber?0:maxNumber.doubleValue; - - if (maxVal < currentValue) { - maxVal = currentValue; - [self.recordStatsMap setObject:@(maxVal) forKey:name]; - [self setStatistic:name withValue:maxVal]; - } + __weak typeof(self) weakSelf = self; + WXPerformBlockOnComponentThread(^{ + NSNumber* maxNumber = [weakSelf.recordStatsMap objectForKey:name]; + double maxVal = nil == maxNumber?0:maxNumber.doubleValue; + + if (maxVal < currentValue) { + maxVal = currentValue; + [weakSelf.recordStatsMap setObject:@(maxVal) forKey:name]; + [weakSelf setStatistic:name withValue:maxVal]; + } + }); } - (void) updateExtInfo:(NSDictionary*) extInfo @@ -294,5 +319,87 @@ - (void) actionImgLoadResult:(bool)succeed withErrorCode:(NSString*)errorCode } } +- (void) recordErrorMsg:(WXJSExceptionInfo *)exception +{ + if (nil == exception || !self.isOpenApm) { + return; + } + __weak typeof(self) weakSelf = self; + WXPerformBlockOnComponentThread(^{ + if (weakSelf.errorList.count > 5) { + [weakSelf.errorList removeObjectAtIndex:0]; + [weakSelf.errorList addObject:exception]; + } + }); +} + +- (NSArray*) topExceptionList +{ + return self.errorList; +} + +- (void) _checkScreenEmptyAndReport +{ + if(self.hasAddView || !self.isStartRender || self.isDegrade){ + return; + } + WXSDKErrCode code = WX_KEY_EXCEPTION_EMPTY_SCREEN_NATIVE; + + + NSNumberFormatter *formater = [[NSNumberFormatter alloc] init]; + formater.numberStyle = NSNumberFormatterDecimalStyle; + + for (WXJSExceptionInfo* exception in self.errorList) { + NSNumber *codeNumber = [formater numberFromString:exception.errorCode]; + if (nil == codeNumber) { + continue; + } + WXSDKErrorGroup group = [WXSDKErrCodeUtil getErrorGroupByCode:codeNumber.intValue]; + if(group == WX_JS){ + code = WX_KEY_EXCEPTION_EMPTY_SCREEN_JS; + break; + } + } + NSString *codeStr = [NSString stringWithFormat:@"%d",code]; + [WXExceptionUtils commitCriticalExceptionRT:self.instanceId errCode:codeStr function:@"_checkScreenEmptyAndReport" + exception:[self _convertTopExceptionListToStr] extParams:nil]; + +} + +- (NSString *)_convertTopExceptionListToStr +{ + if (!self.isOpenApm) { + return @""; + } + if (!self.errorList || self.errorList.count <= 0) { + return @"emptyTopErrorlist"; + } + NSString* errorStr = @"========== topErrorList start ============"; + for (WXJSExceptionInfo* exception in self.errorList) { + errorStr = [errorStr stringByAppendingFormat: + @"%@ -> code:%@,func:%@,exception:%@ \n", + errorStr,exception.errorCode,exception.functionName,exception.exception + ]; + } + errorStr = [errorStr stringByAppendingFormat:@"%@ ========== topErrorList start end ============\n",errorStr]; + return errorStr; +} + +- (NSDictionary*) stageDic +{ + return self.recordStageMap; +} + +- (BOOL) _loadApmSwitch +{ + BOOL openApm = YES; + id configCenter = [WXSDKEngine handlerForProtocol:@protocol(WXConfigCenterProtocol)]; + + if ([configCenter respondsToSelector:@selector(configForKey:defaultValue:isDefault:)]) { + openApm = [[configCenter configForKey:@"iOS_weex_ext_config.open_apm" defaultValue:@(YES) isDefault:NULL] boolValue]; + } + return openApm; +} + @end