Eliminate Calling Error() in Release (when possible)

magic

Active Member
3rd Party Tool Dev
Joined
Mar 5, 2018
GooglePlay set a Bad behavior threshold for quality purposed. ANRs set to 0.48% where CRASH around 1.09%
Meaning 99% of your app instances must not crash and 99.5% of your app must not commit any ANR!

When your ANR or CRASH above this threshold, your app rank will be effected (decrease). This lead to lower download for you app.

I found a lot of our internal module code call this Error() function to report some run-time error. For example:

In CerberusStore class, many method like AddProducts,OpenStoreAsync,BuyProductAsync..etc calling it

Cerberus X:
Method BuyProductAsync:Void( product:Product,onComplete:IOnBuyProductComplete )

    If _state<0 Error "Store unavailable"
    If _state=0 Error "Store not open"
    If _state<>1 Error "Store currently busy"
    
    ......
    
End
I think some of them can be avoided because this increase our CRASH amount. This is an example of the crash due to
- Store unavailable or
- Store not open or
- Store currently busy
Cerberus X:
java.lang.RuntimeException:
  at com.nocom.game.bb_std_lang.error (bb_std_lang.java:223)
  at com.nocom.game.c_CerberusStore.p_GetOwnedProductsAsync (c_CerberusStore.java:26488)
  at com.nocom.game.c_Game.p_OnUpdate (c_Game.java:12466)
  at com.nocom.game.c_GameDelegate.UpdateGame (c_GameDelegate.java:21236)
  at com.nocom.game.BBGame.UpdateGame (BBGame.java:682)
  at com.nocom.game.BBAndroidGame.UpdateGame (BBAndroidGame.java:1407)
  at com.nocom.game.BBAndroidGame.onDrawFrame (BBAndroidGame.java:1477)
  at android.opengl.GLSurfaceView$GLThread.guardedRun (GLSurfaceView.java:1531)
  at android.opengl.GLSurfaceView$GLThread.run (GLSurfaceView.java:1248)
Well, I'm not really expert about thing like ..Java and Google store SDK, but I hope to have an option to not calling Error() whenever possible.
I think, if the app can continue we should not call Error()

What do you think guys
 

Phil7

Active Member
Joined
Jun 26, 2017
I am also no expert in this area, so just an opinion from outside.
This Error function evokes a crash or something that could be evaluated as ANR?
Then I am with you. It might have been the easiest way to implement the CerberusStore, like just exit if something happens along the way.
 

magic

Active Member
3rd Party Tool Dev
Joined
Mar 5, 2018
This Error function evokes a crash or something that could be evaluated as ANR?
Actually it consider as a CRASH.

I got a lot of ANR (App Not Response) also. This is more serious than crash. My rate is above bad behavior threshold. It is 0.72% :(
I don't know what to do because the report didn't tell where is actually the problem is. Normally ANR bug report cannot pin point where (in the code) that the error happen. I still struggle to find where the error is. I can see some method in the report like onSensorChanged, dispatchSensorEvent but I don't think it is. (This is a little bit off topic ;))

Why I think this is so serious? you might wonder. I think, because google do take bad behavior as important factor for ranking. If our app fail on this, it will result poor discoverability on the Play Store
https://developer.android.com/topic/performance/vitals/
 

MikeHart

Administrator
Staff member
Joined
Jun 19, 2017
Location
Germany
I think the call of Error here can be redesigned.
Abput ANRs. Do you load a lot of assets inside OnCreate? I could see this causing problems.
 

magic

Active Member
3rd Party Tool Dev
Joined
Mar 5, 2018
Do you load a lot of assets inside OnCreate?
Already inside OnUpdate(). I also try to avoid long operation inside OnRender() because I think it run inside UI thread.
But I not sure.

ANR happen when the UI thread of an Android app is blocked for more than 5 seconds. I wonder which one of our method actually run in UI thread.
OnRender, OnUpdate, OnCreate? anybody know? I'm really confuse about all this java thing. I not even sure what is UI thread.
 

MikeHart

Administrator
Staff member
Joined
Jun 19, 2017
Location
Germany
I hope you are loading inside OnUpdate not in one batch. Because that could cause the issue that your app is not rendering for more than 5 seconds, depending on how much you load.
 

Phil7

Active Member
Joined
Jun 26, 2017
I often had some crashes when loading larger images. On some devices there is a limit of 1024x1024.
I also ran into an issue regarding free memory of my device. It could be possible that some crashes just occur because the device runs out of memory. Maybe this could be checked upfront somewhere to avoid the crash.
Don't take these thoughts to seriously, I'm just wild guessing here.
 

SLotman

Member
Joined
Jul 3, 2017
I have also seen those 1024x1024 only devices.

Android has all kinds of problems. I once had a game crashing one 1 device, couldn't figure out why.
The culprit was a text file: that device couldn't load any text file with more than 1Mb and it would crash. I splitted the file into 2, and everything worked all right.

Android on moble is the same as Windows for computers. It runs on all sorts of devices, some with really bad limitations.
 

magic

Active Member
3rd Party Tool Dev
Joined
Mar 5, 2018
This is a list of where runtime error checking is done and call the Error(). I know a lot of them must be useful to just stop execution!
But if you have free time and willing to help checking them out, this is the list.

Code:
Location ----> D:\Cerberus\modules\brl\asyncevent.cxs

    Function AddAsyncEventSource:Void( source:IAsyncEventSource )
        If _sources.Contains( source ) Error "Async event source is already active"
        _sources.Push source
    End

Location ----> D:\Cerberus\modules\brl\cerberusstore.cxs

    Method AddProducts:Void( ids:String[],type:Int )
        If _state<0 Error "Store unavailable"
        If _state<>0 Error "Store already open"
        If type<>1 And type<>2 Error "Invalid product type"
        For Local id:=EachIn ids
            Local p:=New Product
            p.identifier=id
            p.type=type
            _products.Push p
        Next
    End
    Method OpenStoreAsync:Void( onComplete:IOnOpenStoreComplete )
        If _state<0 Error "Store unavailable"
        If _state<>0 Error "Store already open"
        _bbproducts=New BBProduct[_products.Length]
        For Local i:=0 Until _products.Length
            _bbproducts[i]=_products.Get( i )
        Next
        _onOpen=onComplete
        _state=2
        AddAsyncEventSource Self
        _store.OpenStoreAsync( _bbproducts )
    End
    Method BuyProductAsync:Void( product:Product,onComplete:IOnBuyProductComplete )
        If _state<0 Error "Store unavailable"
        If _state=0 Error "Store not open"
        If _state<>1 Error "Store currently busy"
        _buying=product
        _onBuy=onComplete
        _state=3
        AddAsyncEventSource Self
        _store.BuyProductAsync( product )
    End
    Method GetOwnedProductsAsync:Void( onComplete:IOnGetOwnedProductsComplete )
        If _state<0 Error "Store unavailable"
        If _state=0 Error "Store not open"
        If _state<>1 Error "Store currently busy"
        _onGetOwned=onComplete
        _state=4
        AddAsyncEventSource Self
        _store.GetOwnedProductsAsync()
    End
    Method GetProducts:Product[]( type:Int )
        If type<>1 And type<>2 Error "Invalid product type"
        If type=1 Return _cons
        Return _ncons
    End
    Line 271:Error "INTERNAL ERROR"
    
Location ----> D:\Cerberus\modules\brl\databuffer.cxs

    Method New( length:Int,direct:Bool=False )
        #If LANG="java"
            If Not _New( length,direct ) Error "Allocate DataBuffer failed"
        #Else
            If Not _New( length ) Error "Allocate DataBuffer failed"
        #Endif
    End
    Line 260: Error "Invalid string encoding:"+encoding
    Line 294: Error "Invalid string encoding:"+encoding
    
Location ----> D:\Cerberus\modules\brl\filestream.cxs

    Line 33: If Not _stream Error "Failed to open stream"

Location ----> D:\Cerberus\modules\brl\httprequest.cxs

    Method Open:Void( req:String, url:String, onComplete:IOnHttpRequestComplete, timeout:Int = 10, httpsVerifyCertificate:Bool = False, httpsVerifyHost:Bool = False )
        If _req And _req.IsRunning() Error "HttpRequest in progress"
        _req=New BBHttpRequest
        _onComplete=onComplete
        _req.Open( req, url, timeout, httpsVerifyCertificate, httpsVerifyHost )
    End
    Method SetHeader:Void( name:String,value:String )
        If Not _req Error "HttpRequest not open"
        If _req.IsRunning() Error "HttpRequest in progress"
        _req.SetHeader( name,value )
    End
    Method Send:Void()
        If Not _req Error "HttpRequest not open"
        If _req.IsRunning() Error "HttpRequest in progress"
        AddAsyncEventSource Self
        _req.Send()
    End
    Method Send:Void( data:String,mimeType:String="text/plain;charset=UTF-8",encoding:String="utf8" )
        If Not _req Error "HttpRequest not open"
        If _req.IsRunning() Error "HttpRequest in progress"
        If mimeType _req.SetHeader( "Content-Type",mimeType )
        AddAsyncEventSource Self
        _req.SendText( data,encoding )
    End
    Method Status:Int()
        If Not _req Error "HttpRequest not open"
        Return _req.Status()
    End
    Method ResponseText:String()
        If Not _req Error "HttpRequest not open"
        Return _req.ResponseText()
    End
    Method BytesReceived:Int()
        If Not _req Error "HttpRequest not open"
        Return _req.BytesReceived()
    End

Location ----> D:\Cerberus\modules\brl\socket.cxs

    Method Enqueue:Void( op:AsyncOp )
        queue[put]=op
        put=(put+1) Mod QUEUE_SIZE
        If put=get Error "AsyncQueue queue overflow!"
        Start                       
    End
    Method New( protocol:String="stream" )
        Local proto:Int
        Select protocol
        Case "stream" proto=STREAM
        Case "server" proto=SERVER
        Case "datagram" proto=DATAGRAM
        Default Error "Illegal socket protocol"
        End
        _sock=New BBSocket
        If Not _sock.Open( proto ) Error "Socket open failed"
        _proto=proto
        _state=OPEN
        Start()
    End
    
Location ----> D:\Cerberus\modules\brl\tcpstream.cxs

    Method New( socket:Socket )
        If socket.Protocol<>"stream" Error "Socket must be a stream socket"
        _socket=socket
    End

Location ----> D:\Cerberus\modules\cerberus\list.cxs

    Method Compare( lhs:T,rhs:T )    'This method should be implemented by subclasses for Sort to work
        Error "Unable to compare items"
    End
    
Location ----> D:\Cerberus\modules\cerberus\stack.cxs

    Method Compare:Int( lhs:T,rhs:T )
        Error "Unable to compare items"
    End
    
Location ----> D:\Cerberus\modules\dom\mkwebgl.cxs

    If line.StartsWith( "const GLenum " )
        'eg:    const GLenum DEPTH_BUFFER_BIT               = 0x00000100;
        Local i=line.Find( "=" )
        If i=-1 Error "ERR"
        line=line[13..i].Trim()
        Local proto:="~tField "+line
        If line.EndsWith( "_" ) proto+="=~q"+line[..-1]+"~q"
        protos.Push proto
        Continue
    Endif

    If i=-1 Error "ERR:"+line

    If i=-1 Error "ERR"

    If i=-1 Error "ERR"

    If i=-1 Error "ERR"

Location ----> D:\Cerberus\modules\mojo\app.cxs

    Method New()
        If _app Error "App has already been created"
        _app=Self
        _delegate=New GameDelegate
        _game.SetDelegate _delegate
    End
    Function EndApp:Void()
        Error ""
    End

Location ----> D:\Cerberus\modules\mojo\graphics.cxs

    Function DebugRenderDevice()
        If Not renderDevice Error "Rendering operations can only be performed inside OnRender"
    End
    Method Init:Image( surf:Surface,nframes,iflags )
        If surface Error "Image already initialized"
        surface=surf
            
        width=surface.Width/nframes
        height=surface.Height
        
        frames=New Frame[nframes]
        For Local i=0 Until nframes
            frames[i]=New Frame( i*width,0 )
        Next
        
        ApplyFlags iflags
        Return Self
    End
    Method Init:Image( surf:Surface,x,y,iwidth,iheight,nframes,iflags,src:Image,srcx,srcy,srcw,srch )
        If surface Error "Image already initialized"
        surface=surf
        source=src

        width=iwidth
        height=iheight
        
        frames=New Frame[nframes]
        
        Local ix:=x,iy:=y
        
        For Local i=0 Until nframes
            If ix+width>srcw
                ix=0
                iy+=height
            Endif
            If ix+width>srcw Or iy+height>srch
                Error "Image frame outside surface"
            Endif
            frames[i]=New Frame( ix+srcx,iy+srcy )
            ix+=width
        Next
        
        ApplyFlags iflags
        Return Self
    End
    Function LoadImage:Image( path$,frameCount=1,flags=Image.DefaultFlags )
        Local surf:=device.LoadSurface( FixDataPath(path) )
        If surf
            Return (New Image).Init( surf,frameCount,flags )
        Else
            DebugLog("Error - Unable to load image: " + path)
        Endif
    End
    Function LoadImage:Image( path$,frameWidth,frameHeight,frameCount,flags=Image.DefaultFlags )
        Local surf:=device.LoadSurface( FixDataPath(path) )
        If surf
            Return (New Image).Init( surf,0,0,frameWidth,frameHeight,frameCount,flags,Null,0,0,surf.Width,surf.Height )
        Else
            DebugLog("Error - Unable to load image: " + path)
        Endif
    End

Location ----> D:\Cerberus\modules\mojo\native\mojo.android.java

    if( bitmap==Null ) Throw New Error( "Attempt to use discarded image" );

    if( texId==0 ) Throw New Error( "glGenTextures failed" );

    if( bitmap==Null ) Throw New Error( "Attempt to use discarded image" );

    if( texId==0 ) Throw New Error( "glGenTextures failed" );

Location ----> D:\Cerberus\modules\mojo2\glutil.cxs

    Function glCompile:Int( type:Int,source:String )
        #If TARGET<>"glfw" Or GLFW_USE_ANGLE_GLES20
            source="precision mediump float;~n"+source
        #Endif
        Local shader:=glCreateShader( type )
        glShaderSource shader,source
        glCompileShader shader
        glGetShaderiv shader,GL_COMPILE_STATUS,tmpi
        If Not tmpi[0]
            Print "Failed to compile fragment shader:"+glGetShaderInfoLog( shader )
            Local lines:=source.Split( "~n" )
            For Local i:=0 Until lines.Length
                Print (i+1)+":~t"+lines[i]
            Next
            Error "Compile fragment shader failed"
        Endif
        Return shader
    End
    Function glLink:Void( program:Int )
        glLinkProgram program
        glGetProgramiv program,GL_LINK_STATUS,tmpi
        If Not tmpi[0] Error "Failed to link program:"+glGetProgramInfoLog( program )
    End

Location ----> D:\Cerberus\modules\mojo2\graphics.cxs

    If Not defaultFont Error "Can't load default font"

    Method Retain:Void()
        If _refs<=0 Error "Internal error"
        _refs+=1
    End
    Method Release:Void()
        If _refs<=0 Error "Internal error"
        _refs-=1
        If _refs Return
        _refs=-1
        Destroy
    End
    
    If format<>4 Error "Invalid texture format: "+format

    If glCheckFramebufferStatus( GL_FRAMEBUFFER )<>GL_FRAMEBUFFER_COMPLETE Error "Incomplete framebuffer"

    If i0=-1 Error "Can't find //@vertex chunk"
    
    If i1=-1 Error "Can't find //@fragment chunk"
    
    Error "Unsupported uniform type: name="+u.name+", location="+u.location+", size="+u.size+", type="+u.type
    
    If Not texture Error "Material has no ColorTexture"

    If Not texture Error "Material has no ColorTexture"

    If Not (_texture.Flags & Texture.RenderTarget) Error "Texture is not a render target texture"

    If Not (_texture.Flags & Texture.RenderTarget) Error "Texture is not a render target texture"

    Error "RenderTarget object must an Image, a Texture or Null"

    Error "Invalid TransformCoords mode"

Location ----> D:\Cerberus\modules\opengl\native\gles11.android.java

    Throw New Error( "Out of memory error loading bitmap" );

Location ----> D:\Cerberus\modules\opengl\native\gles20.android.java

    Throw New Error( "Out of memory error loading bitmap" );

    Throw New Error( "Out of memory error loading bitmap" );

Location ----> D:\Cerberus\modules\reflection\reflection.cxs

    Method ArrayLength:Int( inst:Object )
        Error "Class is not an array class"
    End
    Method GetElement:Object( inst:Object,index )
        Error "Class is not an array class"
    End
    Method SetElement:Void( inst:Object,index,value:Object )
        Error "Class is not an array class"
    End
    Method NewInstance:Object()
        Error "Can't create instance of class"
    End
    Method NewArray:Object( length )
        Error "Can't create instance of array"
    End

Location ----> D:\Cerberus\modules\reflection\reflector.cxs

    If Not refmod Error "reflection module not found!"
 
Last edited:

SLotman

Member
Joined
Jul 3, 2017
Have you tried creating a Function Error(str:String) that does nothing (or output it to console)?

Yup, just tried it here, it works:

Code:
Function Error:Int(str:String)
    Print ">>>>> CALLED MY ERROR!"
End Function
Doing a Error "don't crash me!" will call the function above :)
 

magic

Active Member
3rd Party Tool Dev
Joined
Mar 5, 2018
I think this just function overloading it. When you call from your code it call you version. but not from module
 
Top Bottom