[starvation] streamline and enable strict mode

Summary:
The 2nd iteration of analysis of the Android core implementation did not yield actionable models, so delete those.

Turn on strict-mode reporting by default, when doing starvation analysis (which is disabled by default).

Reviewed By: jvillard

Differential Revision: D9991448

fbshipit-source-id: 67504591d
master
Nikos Gorogiannis 6 years ago committed by Facebook Github Bot
parent ee68f035ff
commit 2989b339a1

@ -1412,10 +1412,9 @@ INTERNAL OPTIONS
Specify combinations of class/method list that should be skipped
during starvation analysis (default: [])
--starvation-strict-mode
Activates: During starvation analysis, report strict mode
violations (Android only) (Conversely:
--no-starvation-strict-mode)
--no-starvation-strict-mode
Deactivates: During starvation analysis, report strict mode
violations (Android only) (Conversely: --starvation-strict-mode)
--stats-report file
Write a report of the analysis results to a file

@ -2075,7 +2075,7 @@ and profiler_samples =
and starvation_strict_mode =
CLOpt.mk_bool ~long:"starvation-strict-mode" ~default:false
CLOpt.mk_bool ~long:"starvation-strict-mode" ~default:true
"During starvation analysis, report strict mode violations (Android only)"

@ -160,7 +160,6 @@ let find_annotated_or_overriden_annotated_method is_annot pname tenv =
let ui_matcher_records =
let open MethodMatcher in
let superclasses = {empty with search_superclasses= Some true} in
let fragment_methods =
(* sort police: this is in lifecycle order *)
[ "onAttach"
@ -175,21 +174,22 @@ let ui_matcher_records =
; "onDestroy"
; "onDetach" ]
in
[ {superclasses with classname= "android.support.v4.app.Fragment"; methods= fragment_methods}
; {superclasses with classname= "android.app.Fragment"; methods= fragment_methods}
; {superclasses with classname= "android.content.ContentProvider"; methods= ["onCreate"]}
; {superclasses with classname= "android.content.BroadcastReceiver"; methods= ["onReceive"]}
; { superclasses with
(* search_superclasses is true by default in how [default] is treated *)
[ {default with classname= "android.support.v4.app.Fragment"; methods= fragment_methods}
; {default with classname= "android.app.Fragment"; methods= fragment_methods}
; {default with classname= "android.content.ContentProvider"; methods= ["onCreate"]}
; {default with classname= "android.content.BroadcastReceiver"; methods= ["onReceive"]}
; { default with
classname= "android.app.Service"
; methods= ["onBind"; "onCreate"; "onDestroy"; "onStartCommand"] }
; { superclasses with
; { default with
classname= "android.app.Activity"
; methods= ["onCreate"; "onStart"; "onRestart"; "onResume"; "onPause"; "onStop"; "onDestroy"]
}
; { superclasses with
; { default with
(* according to Android documentation, *all* methods of the View class run on UI thread, but
let's be a bit conservative and catch all methods that start with "on".
https://developer.android.com/reference/android/view/View.html *)
let's be a bit conservative and catch all methods that start with "on".
https://developer.android.com/reference/android/view/View.html *)
method_prefix= Some true
; classname= "android.view.View"
; methods= ["on"] } ]

@ -1,5 +1,5 @@
(*
* Copyright (c) 2017-present, Facebook, Inc.
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -54,8 +54,12 @@ let of_record {search_superclasses; method_prefix; actuals_pred; classname; meth
|> Staged.unstage
let empty =
{search_superclasses= None; method_prefix= None; actuals_pred= None; classname= ""; methods= []}
let default =
{ search_superclasses= Some true
; method_prefix= Some false
; actuals_pred= Some (fun _ -> true)
; classname= ""
; methods= [] }
let of_list matchers tenv pn actuals = List.exists matchers ~f:(fun m -> m tenv pn actuals)
@ -82,7 +86,7 @@ let of_json top_json =
| _ ->
error json
in
(match json with `Assoc fields -> parse_fields fields empty | _ -> error json) |> of_record
(match json with `Assoc fields -> parse_fields fields default | _ -> error json) |> of_record
in
match top_json with
| `List matchers_json ->

@ -1,5 +1,5 @@
(*
* Copyright (c) 2017-present, Facebook, Inc.
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -34,8 +34,9 @@ type record =
; classname: string
; methods: string list }
val empty : record
(** record with empty strings, lists and None where appropriate. Useful for [with] expressions *)
val default : record
(** record encapsulating the default arguments of [call_matches]. [classname=""] and [methods=[]].
Useful for [with] expressions *)
val of_record : record -> t
(** make a matcher out of a record; optional values use defaults *)

@ -94,22 +94,23 @@ let empty_or_excessive_timeout actuals =
let strict_mode_matcher =
let open MethodMatcher in
let open StarvationDomain.Event in
(* NB [empty] searches superclasses too. Most of the classes below are final and we don't
(* NB [default] searches superclasses too. Most of the classes below are final and we don't
really want to search superclasses for those that aren't, so for performance, disable that *)
let empty = {empty with search_superclasses= Some false} in
let dont_search_superclasses = {default with search_superclasses= Some false} in
let matcher_records =
[ { empty with
[ { dont_search_superclasses with
classname= "dalvik.system.BlockGuard$Policy"; methods= ["on"]; method_prefix= Some true }
; {empty with classname= "java.lang.System"; methods= ["gc"; "runFinalization"]}
; {empty with classname= "java.lang.Runtime"; methods= ["gc"]}
; {empty with classname= "java.net.Socket"; methods= ["connect"]}
; { dont_search_superclasses with
classname= "java.lang.System"; methods= ["gc"; "runFinalization"] }
; {dont_search_superclasses with classname= "java.lang.Runtime"; methods= ["gc"]}
; {dont_search_superclasses with classname= "java.net.Socket"; methods= ["connect"]}
(* all public constructors of Socket with two or more arguments call connect *)
; { empty with
; { dont_search_superclasses with
classname= "java.net.Socket"
; methods= [Typ.Procname.Java.constructor_method_name]
; actuals_pred= Some (function [] | [_] -> false | _ -> true) }
; {empty with classname= "java.net.DatagramSocket"; methods= ["connect"]}
; { empty with
; {dont_search_superclasses with classname= "java.net.DatagramSocket"; methods= ["connect"]}
; { dont_search_superclasses with
classname= "java.io.File"
; methods=
[ "canRead"
@ -136,9 +137,7 @@ let strict_mode_matcher =
; "setReadOnly"
; "setWritable" ] } ]
in
let matcher =
of_list (StrictModeModels.is_strict_mode_violation :: List.map matcher_records ~f:of_record)
in
let matcher = of_list (List.map matcher_records ~f:of_record) in
(matcher, High)
@ -146,37 +145,37 @@ let standard_matchers =
let open MethodMatcher in
let open StarvationDomain.Event in
let high_sev =
[ {empty with classname= "java.lang.Thread"; methods= ["sleep"]}
; { empty with
[ {default with classname= "java.lang.Thread"; methods= ["sleep"]}
; { default with
classname= "java.lang.Object"
; methods= ["wait"]
; actuals_pred= Some empty_or_excessive_timeout }
; { empty with
; { default with
classname= "java.util.concurrent.CountDownLatch"
; methods= ["await"]
; actuals_pred= Some empty_or_excessive_timeout }
(* an IBinder.transact call is an RPC. If the 4th argument (5th counting `this` as the first)
is int-zero then a reply is expected and returned from the remote process, thus potentially
blocking. If the 4th argument is anything else, we assume a one-way call which doesn't block. *)
; { empty with
; { default with
classname= "android.os.IBinder"
; methods= ["transact"]
; actuals_pred=
Some
(fun actuals ->
List.nth actuals 4 |> Option.value_map ~default:false ~f:HilExp.is_int_zero ) }
; { empty with
; { default with
classname= "android.accounts.AccountManager"
; methods= ["setUserData"]
; search_superclasses= Some false } ]
in
let low_sev =
[ { empty with
[ { default with
classname= "java.util.concurrent.Future"
; methods= ["get"]
; actuals_pred= Some empty_or_excessive_timeout
; search_superclasses= Some false }
; { empty with
; { default with
classname= "android.os.AsyncTask"
; methods= ["get"]
; actuals_pred= Some empty_or_excessive_timeout } ]

@ -1,263 +0,0 @@
(*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
open MethodMatcher
(* frameworks/base/core/java/android/app/backup/BackupAgent.java *)
let is_BackupAgent_method =
call_matches "android.app.backup.BackupAgent"
["onRestoreFile" (* onRestoreFile(ParcelFileDescriptor,long,int,String,String,long,long) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/app/DownloadManager.java *)
let is_DownloadManager_method =
call_matches "android.app.DownloadManager" ["rename" (* rename(Context,long,String) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/app/NotificationManager.java *)
let is_NotificationManager_method =
call_matches "android.app.NotificationManager"
["notifyAsUser" (* notifyAsUser(String,int,Notification,UserHandle) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/content/pm/ActivityInfo.java *)
let is_ActivityInfo_method =
call_matches "android.content.pm.ActivityInfo" ["dump" (* dump(Printer,String,int) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/content/pm/ApplicationInfo.java *)
let is_ApplicationInfo_method =
call_matches "android.content.pm.ApplicationInfo"
[ "dump"
; (* dump(Printer,String,int) *)
"getHiddenApiEnforcementPolicy"
; (* getHiddenApiEnforcementPolicy() *)
"maybeUpdateHiddenApiEnforcementPolicy"
(* maybeUpdateHiddenApiEnforcementPolicy(int,int) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/content/pm/ProviderInfo.java *)
let is_ProviderInfo_method =
call_matches "android.content.pm.ProviderInfo" ["dump" (* dump(Printer,String,int) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/content/pm/ResolveInfo.java *)
let is_ResolveInfo_method =
call_matches "android.content.pm.ResolveInfo" ["dump" (* dump(Printer,String,int) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/content/pm/ServiceInfo.java *)
let is_ServiceInfo_method =
call_matches "android.content.pm.ServiceInfo" ["dump" (* dump(Printer,String,int) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/database/sqlite/SQLiteDatabase.java *)
let is_SQLiteDatabase_method =
call_matches "android.database.sqlite.SQLiteDatabase"
[ "addCustomFunction"
; (* addCustomFunction(String,int,SQLiteDatabase$CustomFunction) *)
"reopenReadWrite"
(* reopenReadWrite() *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/ddm/DdmHandleHeap.java *)
let is_DdmHandleHeap_method =
call_matches "android.ddm.DdmHandleHeap" ["handleChunk" (* handleChunk(Chunk) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/net/Uri.java *)
let is_Uri_method =
call_matches "android.net.Uri" ["getCanonicalUri" (* getCanonicalUri() *)
] |> Staged.unstage
(* frameworks/base/core/java/android/os/Environment.java *)
let is_Environment_method =
call_matches "android.os.Environment"
["classifyExternalStorageDirectory" (* classifyExternalStorageDirectory(File) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/os/Parcel.java *)
let is_Parcel_method =
call_matches "android.os.Parcel" ["readExceptionCode" (* readExceptionCode() *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/os/RecoverySystem.java *)
let is_RecoverySystem_method =
call_matches "android.os.RecoverySystem"
[ "handleAftermath"
; (* handleAftermath(Context) *)
"rebootPromptAndWipeUserData"
; (* rebootPromptAndWipeUserData(Context,String) *)
"rebootWipeCache"
; (* rebootWipeCache(Context,String) *)
"rebootWipeUserData"
; (* rebootWipeUserData(Context,boolean) *)
"rebootWipeUserData"
; (* rebootWipeUserData(Context,boolean,String,boolean) *)
"rebootWipeUserData"
; (* rebootWipeUserData(Context,boolean,String,boolean,boolean) *)
"rebootWipeUserData"
(* rebootWipeUserData(Context,String) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/os/storage/StorageManager.java *)
let is_StorageManager_method =
call_matches "android.os.storage.StorageManager"
[ "getPrimaryStoragePathAndSize"
; (* getPrimaryStoragePathAndSize() *)
"getPrimaryStorageSize"
; (* getPrimaryStorageSize() *)
"getStorageBytesUntilLow"
; (* getStorageBytesUntilLow(File) *)
"getStorageCacheBytes"
; (* getStorageCacheBytes(File,int) *)
"getStorageLowBytes"
(* getStorageLowBytes(File) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/os/StrictMode.java *)
let is_StrictMode_method =
call_matches "android.os.StrictMode"
[ "conditionallyCheckInstanceCounts"
; (* conditionallyCheckInstanceCounts() *)
"decrementExpectedActivityCount"
; (* decrementExpectedActivityCount(Class) *)
"noteDiskRead"
; (* noteDiskRead() *)
"noteDiskWrite"
; (* noteDiskWrite() *)
"noteResourceMismatch"
; (* noteResourceMismatch(Object) *)
"noteUnbufferedIO"
; (* noteUnbufferedIO() *)
"queueIdle"
(* queueIdle() *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/util/AtomicFile.java *)
let is_AtomicFile_method =
call_matches "android.util.AtomicFile"
["getLastModifiedTime"; (* getLastModifiedTime() *) "startWrite" (* startWrite(long) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/webkit/WebViewFactory.java *)
let is_WebViewFactory_method =
call_matches "android.webkit.WebViewFactory"
["onWebViewProviderChanged" (* onWebViewProviderChanged(PackageInfo) *)
]
|> Staged.unstage
(* frameworks/base/core/java/android/webkit/WebViewLibraryLoader.java *)
let is_WebViewLibraryLoader_method =
call_matches "android.webkit.WebViewLibraryLoader"
["getWebViewNativeLibrary" (* getWebViewNativeLibrary(PackageInfo,boolean) *)
]
|> Staged.unstage
(* frameworks/base/media/java/android/media/MiniThumbFile.java *)
let is_MiniThumbFile_method =
call_matches "android.media.MiniThumbFile"
[ "eraseMiniThumb"
; (* eraseMiniThumb(long) *)
"getMagic"
; (* getMagic(long) *)
"getMiniThumbFromFile"
; (* getMiniThumbFromFile(long,byte[]) *)
"saveMiniThumbToFile"
(* saveMiniThumbToFile(byte[],long,long) *)
]
|> Staged.unstage
(* frameworks/base/media/java/android/media/RingtoneManager.java *)
let is_RingtoneManager_method =
call_matches "android.media.RingtoneManager"
["deleteExternalRingtone" (* deleteExternalRingtone(Uri) *)
]
|> Staged.unstage
(* frameworks/multidex/library/src/androidx/multidex/MultiDex.java *)
let is_MultiDex_method =
call_matches "androidx.multidex.MultiDex"
[ "install"
; (* install(Context) *)
"installInstrumentation"
(* installInstrumentation(Context,Context) *)
]
|> Staged.unstage
(* libcore/ojluni/src/main/java/java/util/logging/FileHandler.java *)
let is_FileHandler_method =
call_matches "java.util.logging.FileHandler" ["run" (* run() *)
] |> Staged.unstage
let is_strict_mode_violation =
let matchers =
[ is_BackupAgent_method
; is_DownloadManager_method
; is_NotificationManager_method
; is_ActivityInfo_method
; is_ApplicationInfo_method
; is_ProviderInfo_method
; is_ResolveInfo_method
; is_ServiceInfo_method
; is_SQLiteDatabase_method
; is_DdmHandleHeap_method
; is_Uri_method
; is_Environment_method
; is_Parcel_method
; is_RecoverySystem_method
; is_StorageManager_method
; is_StrictMode_method
; is_AtomicFile_method
; is_WebViewFactory_method
; is_WebViewLibraryLoader_method
; is_MiniThumbFile_method
; is_RingtoneManager_method
; is_MultiDex_method
; is_FileHandler_method ]
in
fun tenv pn actuals -> List.exists matchers ~f:(fun m -> m tenv pn actuals)

@ -1,11 +0,0 @@
(*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
val is_strict_mode_violation : Tenv.t -> Typ.Procname.t -> HilExp.t list -> bool
(** is the method a potential strict mode violation, given the actuals passed? *)
Loading…
Cancel
Save