[Debug] Skype et CPU élevé

Depuis quelques temps, j’ai remarqué que mon Skype consommait beaucoup de CPU alors qu’il était simplement démarré et connecté à mon compte.

 

On peut le voir sur le screenshot de Process Explorer ci-dessous.

 

image

 

Dans l’onglet “Threads”, on voit qu’un seul thread (TID = 8556) consomme la quasi totalité du CPU utilisé par Skype.

 

image

 

Après avoir pris plusieurs dumps avec ProcDump (Skype semble ne pas trop aimer être breaké par WinDbg, il lance un nombre incalculable d’access violation lors de la libération du processus).

 

Grâce à la commande “!runaway” (informations disponibles ici), on retrouve le même thread qui consomme du CPU.

 

0:051> !runaway
User Mode Time
  Thread       Time
  31:216c      0 days 0:10:27.763
  17:1214      0 days 0:00:16.130
   9:610       0 days 0:00:05.647
   0:1fd4      0 days 0:00:05.226
  35:758       0 days 0:00:01.232
  39:21e8      0 days 0:00:00.904
  38:1e64      0 days 0:00:00.764
  36:1714      0 days 0:00:00.202
  10:10cc      0 days 0:00:00.109
   5:1710      0 days 0:00:00.093
   2:21e4      0 days 0:00:00.093

 

Pour rappel, le TID est donné en hexadécimal avec “!runaway”, il faut le convertir pour retrouver le même TID que celui donné dans Process Explorer.

 

0:051> ? 216c 
Evaluate expression: 8556 = 0000216c

 

On change le thread courant sur la thread 31 (debugger shorthand ID : identifiant donné par le débuggeur au thread). On affiche ensuite la callstack du thread courant.

 

On peut constater que le module “Flash” apparait plusieurs fois sur la callstack. Malheureusement, Adobe ne fournit visiblement pas de symbôles publics pour Flash (malgrès les commentaires de cet article datant de 2010).

C’est pourquoi on aperçoit des offsets énormes sur la callstack.

 

0:051> ~31s

0:031> kb
ChildEBP RetAddr  Args to Child             
WARNING: Stack unwind information not available. Following frames may be wrong.
0af5e300 5e21efc2 14b52c18 000124f8 00000000 Flash+0xfe534
0af5e324 5e21ed3d 00000000 00000000 000003e8 Flash+0xfefc2
0af5e3b0 5e273a5e 1311e0b0 0af5ece8 00000000 Flash+0xfed3d
0af5ecc4 5e272808 0af5ece8 13133040 13133040 Flash+0x153a5e
0af5ed2c 5e31ff7a 13133040 13133040 0af5ed40 Flash+0x152808
0af5eda0 5e31c1b4 00000001 13133040 0aa541b0 Flash+0x1fff7a
0af5edd0 5e32001e 0aa541b0 00000000 00000001 Flash+0x1fc1b4
0af5edf0 5e3c7e13 1311e0b0 00000001 0aa541b0 Flash+0x20001e
0af5eef0 67583e9a 0aa5417c 00000001 00000000 Flash!DllUnregisterServer+0x365b3
0af5ef10 66dbdea5 0a8b3564 0a8b3050 0a8b3564 mshtml!memcpy+0xb98f0
0af5ef38 66f7b1ec 0a8b3050 0a9096b8 00000000 mshtml!CPaintController::NotifyPreRender+0x9e
0af5f1ec 66a3b61a 00008c80 00000000 0a8b3050 mshtml!CView::EnsureView+0x1e21
0af5f40c 66d3c65b 00000001 00000695 0a8b3050 mshtml!CDoc::PaintWorker+0x173
0af5f424 66a39b47 00000001 0a90b530 0abf85f8 mshtml!CDoc::PaintInPlace+0x29
0af5f444 66a36cb3 0090b530 002cedb6 00000000 mshtml!CPaintController::RunRenderingLoop+0x48
0af5f45c 66a366e6 0abf85f8 0abd08a8 00000000 mshtml!CPaintController::OnUpdateBeat+0x25
0af5f47c 66a36e1e 00000000 0abd0870 0a90b500 mshtml!CPaintBeat::OnBeat+0x17a
0af5f494 66a3631a 0a8b306c 00000000 0abd0870 mshtml!CPaintBeat::OnVSyncMethodCall+0x51
0af5f4ec 669f9c19 26772b77 00000000 0000000f mshtml!GlobalWndOnPaintPriorityMethodCall+0x179
0af5f538 756d77d8 002d04f6 000003a2 00000000 mshtml!GlobalWndProc+0xd3
0af5f564 756d90e7 669f9b45 002d04f6 0000000f user32!InternalCallWinProc+0x23
0af5f5e0 756d7b6f 669f9b45 669f9b45 00000000 user32!UserCallWinProcCheckWow+0xdb
0af5f644 756d7c44 038dcce0 00000000 0000000f user32!DispatchClientMessage+0x15d
0af5f680 772c2c92 0af5f698 00000000 0af5f6fc user32!__fnDWORD+0x2b
0af5f694 038dcce0 00000000 0000000f 00000000 ntdll!KiUserCallbackDispatcher+0x2e
0af5f70c 756d8a66 00000000 0000000f 0af5f78c 0x38dcce0
*** WARNING: Unable to verify checksum for C:\Program Files (x86)\Skype\Phone\Skype.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files (x86)\Skype\Phone\Skype.exe
0af5f71c 0254bc85 0af5f72c 00000a00 002d04f6 user32!DispatchMessageW+0x10
0af5f78c 0131c4b1 0af5f7a0 0131c4bb 0af5f7bc Skype+0x127bc85
0af5f7bc 012d70f6 0af5f810 012d6b58 0af5f7d0 Skype+0x4c4b1
0af5f7d0 74a9850d 01221570 0af5f820 772dbf39 Skype+0x70f6
0af5f7dc 772dbf39 01221570 374f47f3 00000000 KERNEL32!BaseThreadInitThunk+0xe
0af5f820 772dbf0c 012d70cc 01221570 ffffffff ntdll!__RtlUserThreadStart+0x72
0af5f838 00000000 012d70cc 01221570 00000000 ntdll!_RtlUserThreadStart+0x1b

 

Si on fait la même opération sur les 2 autres dumps pris, on retrouve également le module “Flash” dans la callstack du thread 31.

 

On peut donc fortement penser que Flash est responsable de la consommation CPU.

 

Après plusieurs tests, je me suis rendu compte que la consommation CPU n’était forte que lorsque Skype était sur sa page d’accueil (ci-dessous) qui contient des publicités.

 

image

 

Un simple clique-droit sur un de ces encarts montre que ces derniers sont en Flash.

image

 

On voit très nettement lorsque Skype est sur son écran d’accueil et lorsqu’on change pour afficher une conversation.

 

image

[Debug] Afficher les chaînes de caractères situées dans un processus ou un dump

Récupérer la liste des chaînes de caractères (string) présentes dans la mémoire utilisée par un exécutable peut être intéressant (Mise en place d’interning par exemple).

Cet article montre plusieurs manières de récupérer ces chaînes de caractères directement dans un processus live ou un dump.

Dans les exemples ci-dessous, l’application console (.NET) récupère un mot situé sur une page web (http://aymericlagier.com/wp-content/uploads/2013/05/word.html). Le but est de retrouver ce mot en mémoire.

 

image

 

Process Explorer

Avec un processus live, Process Explorer donne accès aux chaînes de caractères que ce soit en mémoire ou à l’intérieur du binaire. La récupération des chaînes de caractères dans le binaire fonctionne mais celle en mémoire affiche une liste vide pour les processus .NET.

J’ai tenté de poser la question sur le forum dédié à Process Explorer mais mon message est resté sans réponse : http://forum.sysinternals.com/strings-into-net-memory_topic29299.html.

 

image

 

image

 

Process Hacker

De la même manière que Process Explorer, Process Hacker permet de récupérer les chaînes de caractères en mémoire ou dans le binaire.

Process Explorer dispose d’options plus avancées permettant de filtrer les chaînes de caractères :

– Taille minimum des chaînes à afficher

– Mémoire privée ou mappée

 

En comparant les résultats de Process Hacker avec les résultats de !Strings de SOSEX (plus loin dans l’article), on s’aperçoit qu’il ne cherche pas les objets .NET mais fait simplement une recherche des chaînes imprimables en mémoire (voir section sur la commande “s” plus loin dans l’article). Si une des chaîne est contenue dans un objet .NET (comme par exemple une instance de String ou de Byte[]), un “!lno <adresse>” (SOS) permet de retrouver l’objet .NET associé.

 

image

 

image

 

Une fois ces premiers filtres appliqués, il est possible de filtrer sur le contenu des chaînes de caractères.

 

image

 

En appliquant un filtre “Contains” sur le mot “SingleWord”, on retrouve 8 occurrences de notre chaîne caractères.

 

image

 

Strings.exe

Avec un dump du processus, il est possible de récupérer les chaînes de caractères avec Strings.exe et de les exporter dans un fichier texte pour une éventuelle analyze dans Excel ou autre.

 

Un seul filtre est disponible dans Strings.exe, la taille minimale des chaînes de caractères (3 par défaut).

 

C:\>strings.exe -n 10 -q  D:\Temp\StringsApplication.dmp > D:\Temp\StringsApplication.txt

 

Explications :

– n 10

Permet de ne récupérer que les chaînes de plus de 10 caractères

-q

Retire l’entête que met Strings.exe par défaut lorsqu’il affiche la liste des chaînes.

SOSEX

SOSEX, l’extension développée par Steve Johnson pour faire du debug .NET dans WinDbg propose la commande !strings pour récupérer les chaines de caractères dans un processus live ou un dump (.NET).

 

Les paramètres de !strings sont intéressants puisqu’ils permettent d’appliquer quelques filtres un peu plus avancés comme le choix de la génération minimale des chaînes ou encore la taille maximale des chaînes à récupérer.

Un autre avantage de SOSEX est de pouvoir utiliser les chaînes trouvées pour effectuer d’autres opérations en mémoire (récupération des roots des chaînes avec gcroot par exemple).

 

Attention, !Strings ne cherche que les chaînes dans le “managed heap”, c’est à dire la mémoire .NET.

 

0:000> !strings /m:SingleWord /n:10 /x:20
Address    Gen    Length   Value
—————————————
029e0b6c    0         10   SingleWord
—————————————
1 matching string

 

Explications:

/m:<Text>

Permet de spécifier le pattern à chercher

/n:<Int>

Taille minimale de la chaîne de caractères

/x:<Int>

Taille maximale de la chaîne de caractères

Recherche en mémoire avec WinDbg

 

La command “s” disponible nativement dans WinDbg permet de rechercher un pattern en mémoire.

Ici on peut s’en servir avec les options “sa” (ASCII) et “su” (Unicode) pour rechercher notre chaîne de caractères.

 

0:000> s -[w]u 0x0 L?0xFFFFFFFF "SingleWord"
029e0b74  0053 0069 006e 0067 006c 0065 0057 006f  S.i.n.g.l.e.W.o.
029e0eec  0053 0069 006e 0067 006c 0065 0057 006f  S.i.n.g.l.e.W.o.
029e1230  0053 0069 006e 0067 006c 0065 0057 006f  S.i.n.g.l.e.W.o.
0:000> s -[w]a 0x0 L?0xFFFFFFFF "SingleWord"
029d74e7  53 69 6e 67 6c 65 57 6f-72 64 00 00 00 00 00 00  SingleWord……
029e04b8  53 69 6e 67 6c 65 57 6f-72 64 00 00 00 00 00 00  SingleWord……
029e08c4  53 69 6e 67 6c 65 57 6f-72 64 00 00 00 00 00 80  SingleWord……
029e09f8  53 69 6e 67 6c 65 57 6f-72 64 00 00 00 00 00 00  SingleWord……
029e10f8  53 69 6e 67 6c 65 57 6f-72 64 0d 0a 00 00 00 00  SingleWord……

 

Explications:

[w]

Permet de ne chercher que dans la mémoire accessible en écriture.

0x0

Adresse de début de la recherche

L?0xFFFFFFFF

Taille de la recherche

Si on compare ces résultats à ceux donnés par Process Hacker, on retrouve le même nombre d’occurrences mais avec des adresses mémoires différentes. Si on regarde plus attentivement la taille des chaînes affichées dans Process Hacker, on s’aperçoit que les chaînes avec une longueur de 10 (taille de “SingleWord”) ont la même adresse mémoire de chaque côté.

Pour retrouver les adresses mémoires il faut faire un peu de calcul. Si on prend l’adresse 0x029d74e7 affichée par la commande “s”

 

0:000> s -[w]a 0x0 L?0xFFFFFFFF "SingleWord"

 

et que l’on regarde ce qu’il y a juste avant,

 

0:000> ? 029d74e7-0x20
Evaluate expression: 43873479 = 029d74c7
0:000> da 029d74c7
029d74c7  "bytes..Vary: Accept-Encoding…."
029d74e7  "SingleWord"

 

on retrouve la chaîne “Accept-encoding” présente également dans le 1er résultat remonté par Process Hacker.

 

image

 

Pour retrouver l’adresse mémoire présentée par Process Hacker dans WinDbg, il faut simplement remonter de la taille indiquée par Process Hacker (365), y soustraire la taille de “SingleWord” (10) et faire la soustraction avec l’adresse indiquée par la commande “s”. On doit donc partir de 0x029d74e7 et remonter de 355, soit 0x163.

 

0:000> ? 0n355
Evaluate expression: 355 = 00000163
0:000> ? 0x029d74e7-0x163
Evaluate expression: 43873156 = 029d7384
0:000> da 029d7384
029d7384  "HTTP/1.1 200 OK..Set-Cookie: 60g"
029d73a4  "pBAK=R1224199043; path=/; expire"
029d73c4  "s=Sat, 01-Jun-2013 13:09:57 GMT."
029d73e4  ".Date: Sat, 01 Jun 2013 12:06:17"
029d7404  " GMT..Content-Type: text/html..C"
029d7424  "ontent-Length: 10..Connection: k"
029d7444  "eep-alive..Set-Cookie: 60gp=R233"
029d7464  "7168536; path=/; expires=Sat, 01"
029d7484  "-Jun-2013 13:19:02 GMT..Server: "
029d74a4  "Apache/2.2.X (OVH)..Accept-Range"
029d74c4  "s: bytes..Vary: Accept-Encoding."
029d74e4  "…SingleWord"

 

On retrouve bien en vert l’adresse indiquée par Process Hacker.

 

CLrMD

Depuis début mai, une nouvelle librairie .NET fournie par Microsoft permet d’analyser des dumps ou des processus lives. Des informations sur cette librairie sont disponibles ici (FR) et ici (EN).

 

Grâce à cette librairie, il est très facile de récupérer tous les objets .NET, de vérifier leurs types, récupérer leurs valeurs, etc.

 

Comme pour SOSEX on ne cherche ici que dans le “managed heap” (mémoire .NET).

 

using (DataTarget dataTarget = 
    DataTarget.LoadCrashDump(@"D:\Temp\StringsApplication.dmp"))
{
    String dacLocation = dataTarget.ClrVersions[0].TryGetDacLocation();
    ClrRuntime runtime = dataTarget.CreateRuntime(dacLocation);

    ClrHeap heap = runtime.GetHeap();
    foreach (ulong obj in heap.EnumerateObjects())
    {
        ClrType type = heap.GetObjectType(obj);

        if (type.Name.Equals("System.String") == true)
        {
            String value = type.GetValue(obj).ToString();
            if (value != null && value.Contains("SingleWord") == true)
            {
                ulong size = type.GetSize(obj);
                Console.WriteLine("{0,12:X} {1,8:n0} {2} {3}", 
                                  obj, 
                                  size, 
                                  type.Name, 
                                  value); 
            }
        }
    }
}

Console.WriteLine("Finished");
Console.ReadKey();

 

Avec cette librairie, on peut imaginer beaucoup de possibilités, le dé doublonnage, l’export directement dans Excel pour ressortir des statistiques, etc.

[Channel9] Webcasts sur le debugging

Show très intéressant sur le debugging animé par Larry Larsen, Chad Beeder et Andrew Richards composé de webcasts d’une trentaine de minutes qui paraissent toutes les semaines.

 

Les animateurs présentent les outils de la suites SysInternals, expliquent comment utiliser WinDBG pour debugger les applications .NET et natives, analyser des dumps de BSOD, etc…

 

La plupart des concepts sont expliqués assez clairement même si certains épisodes (notamment sur les drivers) ont un niveau technique assez élevé.

 

Voici la liste des épisodes disponibles à l’heure actuelle :

 

Defrag Tools: #1 – Building your USB thumbdrive

Defrag Tools: #2 – Process Explorer

Defrag Tools: #3 – Process Monitor

Defrag Tools: #4 – Process Monitor – Examples

Defrag Tools: #5 – Autoruns and MSConfig

Defrag Tools: #6 – RAMMap

Defrag Tools: #7 – VMMap

Defrag Tools: #8 – Mark Russinovich

Defrag Tools: #9 – ProcDump

Defrag Tools: #10 – ProcDump – Triggers

Defrag Tools: #11 – ProcDump – Windows 8 & Process Monitor

Defrag Tools: #12 – TaskMgr and ResMon

Defrag Tools: #13 – WinDbg

Defrag Tools: Live – //build/ 2012

Defrag Tools: #14 – WinDbg – SOS

Defrag Tools: #15 – WinDbg – Bugchecks (BSOD)

Defrag Tools: #16 – WinDbg – Driver Verifier

Defrag Tools: #17 – WinDbg – Driver Verifier – Part 2

Defrag Tools: #18 – WinDbg – Driver Verifier – Part 3

Defrag Tools: #19 – WinDbg – OCA

Defrag Tools: #20 – WinDbg – Basic Commands

Defrag Tools: #21 – WinDbg – Memory User Mode

[WinDBG] Rendre les adresses mémoire cliquables

Petite astuce donnée dans l’émission DefragTools rendant l’utilisation de WinDBG un peu plus “clicky clicky” avec la version de SOS pour .NET 4.

 

Par défaut, lorsqu’on affiche les objets de la stack .NET avec !dso par exemple, les adresses mémoire pointant vers les objets ne sont pas cliquables et il est necessaire de faire un

 

0:000> !DumpObj xxxxxxxx

 

pour accéder au contenu de l’objet :

 

 

En tapant la commande

 

0:000> .prefer_dml 1

 

on active les liens pour certaines commandes

 

 

On peut maintenant cliquer sur les diffèrentes adresses mémoire pour voir les objets en mémoire.

 

Note : Les liens peuvent aussi être activés unitairement sur un appel de commande grâce au /d. La plupart des commandes de SOS pour .NET 4 l’implémentent.

 

 

Note2 : L’affichage des liens est presque inutilisable avec les commandes qui affiche un très grand nombre de résultat (!dumpheap sans le –stat par exemple).

 

Resources :