Xamarin.Profilerでメモリリーク解析2(Android)

※[悲報]
下記のスクリプト、Xamarin Profilerの0.32以前でないと動かないようです…
そのうち直すかもですが、元のライブラリーが対応してくれないと難しいかもです…(;´・ω・)


Xamarin.Profilerでメモリリーク解析(Android) - omanuke-ekunamoの日記の続きでSnapshot間のオブジェクトの増減を見れるようにしました。

のような場合でSnapshot8と12の間の増減を見たい場合、

dumpDiff<|heapDiff 8 12

とすると

========= Heap Diff 8 to 12 =================================
[13807] AppCommon.Controls.CustomNavBtnItem  3
[791] Java.Interop.JniPeerMembers.JniInstanceFields  3
[790] Java.Interop.JniPeerMembers.JniInstanceMethods  3
[793] Java.Interop.JniPeerMembers.JniStaticFields  3
[792] Java.Interop.JniPeerMembers.JniStaticMethods  3
[445] Java.Interop.JniType  3
[20868] Microsoft.FSharp.Core.PrintfImpl.Final1@224<Microsoft.FSharp.Core.Unit,System.String,System.String,System.Int32>  3
[2687] System.Action  3
[337] System.AsyncCallback  3
[588] System.Collections.Concurrent.ConcurrentDictionary.Node<System.IntPtr,System.IDisposable>  3
[4567] System.Collections.Generic.Dictionary.Entry<System.String,System.Object>[]  3
[843] System.Collections.Generic.Dictionary<System.Type,Java.Interop.JniPeerMembers.JniInstanceMethods>  3
[13118] System.Collections.Generic.List<Xamarin.Forms.Behavior>  3
[13136] System.Collections.Generic.List<Xamarin.Forms.TriggerBase>  3
[696] System.Collections.Hashtable  3
[3604] System.Collections.Hashtable.bucket[]  3
[13108] System.Collections.ObjectModel.ObservableCollection.SimpleMonitor<Xamarin.Forms.Behavior>  3
[13114] System.Collections.ObjectModel.ObservableCollection.SimpleMonitor<Xamarin.Forms.TriggerBase>  3
[13780] System.Net.Sockets.NetworkStream  3
[2929] System.Random  3
[2922] System.Threading.IThreadPoolWorkItem[]  3
[2908] System.Threading.ThreadPoolWorkQueue.WorkStealingQueue  3
[2928] System.Threading.ThreadPoolWorkQueueThreadLocals  3
[20843] System.Tuple<Microsoft.FSharp.Core.FSharpFunc<Microsoft.FSharp.Core.FSharpFunc<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.PrintfImpl.PrintfEnv<Microsoft.FSharp.Core.Unit,System.String,System.String>>,Microsoft.FSharp.Core.FSharpFunc<System.Int32,System.String>>,System.Int32>  3
[20365] TAC.Model.JsonParseUtil.JsonParser<TAC.Model.CommonType.PosSum[]>  3
[21334] Trading.Chart.BaseTypes.RendererPriority  3
[23710] Trading.Chart.Processor.CalcType  3
[25283] Trading.Chart.Processor.ProcessDataType  3
[20879] Trading.Chart.Proto.ViewType  3
[21224] Trading.Chart.ViewDataState  3
[13101] Xamarin.Forms.AttachedCollection<Xamarin.Forms.Behavior>  3
[13111] Xamarin.Forms.AttachedCollection<Xamarin.Forms.TriggerBase>  3
[12896] Xamarin.Forms.IGestureRecognizer[]  3
[13214] Xamarin.Forms.Image  3
[13211] Xamarin.Forms.TapGestureRecognizer  3
[13833] Android.Views.GestureDetector  4
[1939] Android.Views.MotionEvent  4
[20855] Microsoft.FSharp.Core.PrintfImpl.basicNumberToString@584-220  4
[17874] System.Func<System.Int32,System.Boolean>  4
[17903] System.Lazy.Boxed<Android.Views.GestureDetector>  4
[5131] TAC.Model.JsonParseUtil.JsonParser<System.Boolean>  4
[5491] TAC.Model.TLineMode  4
[14779] Xamarin.Forms.Platform.Android.ImageRenderer  4
[20752] Microsoft.FSharp.Collections.MapTree.MapOne<System.String,Microsoft.FSharp.Collections.FSharpList<System.Tuple<System.String,System.String,Microsoft.FSharp.Collections.FSharpList<System.Tuple<System.String,System.Int32>>>>>  5
[24947] Microsoft.FSharp.Core.PrintfImpl.Final1@224<Microsoft.FSharp.Core.Unit,System.String,System.String,System.Int64>  5

などと[typeId] 型名 増減 という感じで表示します。
怪しい参照などあったら

dumpAllPathByType (hShot 12) 21212

などとみたいSnapshotとtypeIdを指定すると被参照パスみれます。

スクリプトこちら(´・ω・`)

#I @"D:\GitHub\heap-shot\HeapShot.Reader\obj\Debug"
//#I @"d:\Dropbox"
#r "heapshot.reader.dll"

open HeapShot.Reader
open System.IO

let path= @"D:\GitHub\heap-shot\HeapShot"
//let path= @"D:\Dropbox"
let fName=Path.Combine(path,"r_.mlpd")
let omap=new ObjectMapReader(fName)
omap.Read()

let ls=omap.LastSnapshot

let hShot i=omap.HeapShots.[i]
//dummy listener
let listener={new IProgressListener with
                member this.ReportProgress(msg,progress)=
                  printfn"[progress] %s %f" msg progress
                member this.Cancelled=false}
//node in PathTree->o and name
let nodeToO (pTree:PathTree) node=
  let o=pTree.GetNodeObject node
  let name=ls.GetObjectTypeName o
  o,name  
//dump each reference path
let rec dumpPath (pTree:PathTree) depth maxDepth node=
  let tabs="".PadLeft(depth*4)
  let o,name=nodeToO pTree node
  printfn "%s%s(%d)" tabs name o
  if depth<maxDepth then
    pTree.GetChildNodes node
    |>Seq.iter(fun cNode->dumpPath pTree (depth+1) maxDepth cNode)
//dump all reference path
let dumpAllPath (pTree:PathTree) maxDepth=
  pTree.GetRootNodes()
  |>Seq.iter(fun rNode->
    let o,name=nodeToO pTree rNode
    printfn"----[%x] %s ------" o name
    dumpPath pTree 0 maxDepth rNode)
//dump all reference path for type
let dumpAllPathByType (ls:HeapSnapshot) t=
  let pTree=ls.GetRoots(listener,t)
  //dump all with max depth
  dumpAllPath pTree 100
//type,name,object count from type name     
let printTnc (t,n,c)=printfn"[%d] %s  %d" t n c
let dumpTncs tncs=tncs|>Seq.iter printTnc

let tncByType (ls:HeapSnapshot) t=
  let n=ls.GetTypeName t
  let os=ls.GetObjectsByType  t
  let c=os|>Seq.length
  t,n,c
let toAllTncs (ls:HeapSnapshot)=
  ls.GetTypes()
  |>Seq.map (tncByType ls)
  |>Seq.filter(fun(_,_,c)->c>0)
  |>Seq.sortBy(fun(_,n,_)->n)
let tncsByKey ls key=
  toAllTncs ls|>Seq.filter(fun(_,n,_)->n.Contains(key))

//diff
let tncsDiff tncs1 tncs2=
  let rec loop acc tncs1 tncs2=
    match (tncs1,tncs2) with
    |[],[]->acc|>List.rev
    |[],(t,n,c)::ts->loop ((t,n,c)::acc) [] ts
    |(t,n,c)::ts,[]->loop ((t,n,-c)::acc) ts []
    |(t1,n1,c1)::ts1,(t2,n2,c2)::ts2->
      let c=Microsoft.FSharp.Core.Operators.compare n1 n2
      if c=0 then loop((t2,n2,c2-c1)::acc) ts1 ts2
      elif c<0 then loop((t1,n1,-c1)::acc) ts1 tncs2
      else loop((t2,n2,c2)::acc) tncs1 ts2
  loop [] (List.ofSeq tncs1) (List.ofSeq tncs2)
let heapDiff fromI toI=
  printfn"========= Heap Diff %d to %d =================================" fromI toI
  let tncs i=hShot i|>toAllTncs
  tncsDiff (tncs fromI) (tncs toI)
let dumpDiff diff=
  diff
  |>List.filter(fun(_,_,diff)->diff<>0)
  |>List.sortBy(fun(_,_,d)->d)
  |>List.iter printTnc

let dumpTncByType (ls:HeapSnapshot) t=[tncByType ls t]|>dumpTncs


omap.HeapShots.Count


dumpDiff<|heapDiff 8 12
dumpDiff<|heapDiff 3 7

dumpAllPathByType (hShot 23) 21212