1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5// ==++==
6//
7
8//
9// ==--==
10/****************************************************************************
11* STRIKE.C *
12* Routines for the NTSD extension - STRIKE *
13* *
14* History: *
15* 09/07/99 Microsoft Created *
16* *
17* *
18\***************************************************************************/
19#include <windows.h>
20#include <winternl.h>
21#include <winver.h>
22#include <wchar.h>
23
24#define NOEXTAPI
25#define KDEXT_64BIT
26#include <wdbgexts.h>
27#undef DECLARE_API
28#undef StackTrace
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <stdint.h>
33#include <string.h>
34#include <stddef.h>
35
36#include "strike.h"
37// We need to define the target address type. This will be used in the
38// functions that read directly from the debuggee address space, vs. using
39// the DAC tgo read the DAC-ized data structures.
40#include "daccess.h"
41//#include "dbgeng.h"
42
43
44#ifndef STRESS_LOG
45#define STRESS_LOG
46#endif // STRESS_LOG
47#define STRESS_LOG_READONLY
48#include "stresslog.h"
49#include <dbghelp.h>
50
51#include "corhdr.h"
52#include "dacprivate.h"
53
54#define CORHANDLE_MASK 0x1
55#define DEFINE_EXT_GLOBALS
56
57#include "util.h"
58
59#ifndef _ASSERTE
60#ifdef _DEBUG
61#define _ASSERTE(expr) \
62 do { if (!(expr) ) { ExtOut(#expr); DebugBreak(); } } while (0)
63#else // _DEBUG
64#define _ASSERTE(expr)
65#endif // _DEBUG else
66#endif // !_ASSERTE
67
68#ifdef _MSC_VER
69#pragma warning(disable:4244) // conversion from 'unsigned int' to 'unsigned short', possible loss of data
70#pragma warning(disable:4189) // local variable is initialized but not referenced
71#endif // _MSC_VER
72
73struct PlugRecord
74{
75 PlugRecord *next;
76
77 size_t PlugStart;
78 size_t PlugEnd;
79 size_t Delta;
80
81 PlugRecord() { ZeroMemory(this,sizeof(PlugRecord)); }
82};
83
84struct PromoteRecord
85{
86 PromoteRecord *next;
87
88 size_t Root;
89 size_t Value;
90 size_t methodTable;
91
92 PromoteRecord() { ZeroMemory(this,sizeof(PromoteRecord)); }
93};
94
95struct RelocRecord
96{
97 RelocRecord *next;
98
99 size_t Root;
100 size_t PrevValue;
101 size_t NewValue;
102 size_t methodTable;
103
104 RelocRecord() { ZeroMemory(this,sizeof(RelocRecord)); }
105};
106
107struct GCRecord
108{
109 ULONG64 GCCount;
110
111 // BOOL IsComplete() { return bFinished && bHaveStart; }
112
113 PlugRecord *PlugList;
114 RelocRecord *RelocList;
115 PromoteRecord *PromoteList;
116
117 void AddPlug(PlugRecord& p) {
118 PlugRecord *pTmp = PlugList;
119 PlugList = new PlugRecord(p);
120 PlugList->next = pTmp;
121 }
122
123 void AddReloc(RelocRecord& r) {
124 RelocRecord *pTmp = RelocList;
125 RelocList = new RelocRecord(r);
126 RelocList->next = pTmp;
127 }
128
129 void AddPromote(PromoteRecord& r) {
130 PromoteRecord *pTmp = PromoteList;
131 PromoteList = new PromoteRecord(r);
132 PromoteList->next = pTmp;
133 }
134
135 UINT PlugCount() {
136 UINT ret = 0;
137 PlugRecord *Iter = PlugList;
138 while (Iter) {
139 Iter = Iter->next;
140 ret++;
141 }
142 return ret;
143 }
144
145 UINT RelocCount() {
146 UINT ret = 0;
147 RelocRecord *Iter = RelocList;
148 while (Iter) {
149 Iter = Iter->next;
150 ret++;
151 }
152 return ret;
153 }
154
155 UINT PromoteCount() {
156 UINT ret = 0;
157 PromoteRecord *Iter = PromoteList;
158 while (Iter) {
159 Iter = Iter->next;
160 ret++;
161 }
162 return ret;
163 }
164
165 void Clear() {
166
167 PlugRecord *pTrav = PlugList;
168 while (pTrav) {
169 PlugRecord *pTmp = pTrav->next;
170 delete pTrav;
171 pTrav = pTmp;
172 }
173
174 RelocRecord *pTravR = RelocList;
175 while (pTravR) {
176 RelocRecord *pTmp = pTravR->next;
177 delete pTravR;
178 pTravR = pTmp;
179 }
180
181 PromoteRecord *pTravP = PromoteList;
182 while (pTravP) {
183 PromoteRecord *pTmp = pTravP->next;
184 delete pTravP;
185 pTravP = pTmp;
186 }
187
188 ZeroMemory(this,sizeof(GCRecord));
189 }
190
191};
192
193#define MAX_GCRECORDS 500
194UINT g_recordCount = 0;
195GCRecord g_records[MAX_GCRECORDS];
196
197void GcHistClear()
198{
199 for (UINT i=0; i < g_recordCount; i++)
200 {
201 g_records[i].Clear();
202 }
203 g_recordCount = 0;
204}
205
206void GcHistAddLog(LPCSTR msg, StressMsg* stressMsg)
207{
208 if (g_recordCount >= MAX_GCRECORDS)
209 {
210 return;
211 }
212
213 if (strcmp(msg, ThreadStressLog::gcPlugMoveMsg()) == 0)
214 {
215 PlugRecord pr;
216 // this is a plug message
217 _ASSERTE(stressMsg->numberOfArgs == 3);
218 pr.PlugStart = (size_t) stressMsg->args[0];
219 pr.PlugEnd = (size_t) stressMsg->args[1];
220 pr.Delta = (size_t) stressMsg->args[2];
221
222 g_records[g_recordCount].AddPlug(pr);
223 }
224 else if (strcmp(msg, ThreadStressLog::gcRootMsg()) == 0)
225 {
226 // this is a root message
227 _ASSERTE(stressMsg->numberOfArgs == 4);
228 RelocRecord rr;
229 rr.Root = (size_t) stressMsg->args[0];
230 rr.PrevValue = (size_t) stressMsg->args[1];
231 rr.NewValue = (size_t) stressMsg->args[2];
232 rr.methodTable = (size_t) stressMsg->args[3];
233 g_records[g_recordCount].AddReloc(rr);
234 }
235 else if (strcmp(msg, ThreadStressLog::gcRootPromoteMsg()) == 0)
236 {
237 // this is a promote message
238 _ASSERTE(stressMsg->numberOfArgs == 3);
239 PromoteRecord pr;
240 pr.Root = (size_t) stressMsg->args[0];
241 pr.Value = (size_t) stressMsg->args[1];
242 pr.methodTable = (size_t) stressMsg->args[2];
243 g_records[g_recordCount].AddPromote(pr);
244 }
245 else if (strcmp(msg, ThreadStressLog::gcStartMsg()) == 0)
246 {
247 // Gc start!
248 _ASSERTE(stressMsg->numberOfArgs == 3);
249 ULONG64 gc_count = (ULONG64) stressMsg->args[0];
250 g_records[g_recordCount].GCCount = gc_count;
251 g_recordCount++;
252 }
253 else if (strcmp(msg, ThreadStressLog::gcEndMsg()) == 0)
254 {
255 // Gc end!
256 // ULONG64 gc_count = (ULONG64) stressMsg->data;
257 // ExtOut ("ENDGC %d\n", gc_count);
258 }
259}
260
261DECLARE_API(HistStats)
262{
263 INIT_API();
264
265 ExtOut ("%8s %8s %8s\n",
266 "GCCount", "Promotes", "Relocs");
267 ExtOut ("-----------------------------------\n");
268
269 // Just traverse the data structure, printing basic stats
270 for (UINT i=0; i < g_recordCount; i++)
271 {
272 UINT PromoteCount = g_records[i].PromoteCount();
273 UINT RelocCount = g_records[i].RelocCount();
274 UINT GCCount = (UINT) g_records[i].GCCount;
275
276 ExtOut ("%8d %8d %8d\n",
277 GCCount,
278 PromoteCount,
279 RelocCount);
280 }
281
282 BOOL bErrorFound = FALSE;
283
284 // Check for duplicate Reloc or Promote messages within one gc.
285 // Method is very inefficient, improve it later.
286 for (UINT i=0; i < g_recordCount; i++)
287 {
288 { // Promotes
289 PromoteRecord *Iter = g_records[i].PromoteList;
290 UINT GCCount = (UINT) g_records[i].GCCount;
291 while (Iter)
292 {
293 PromoteRecord *innerIter = Iter->next;
294 while (innerIter)
295 {
296 if (Iter->Root == innerIter->Root)
297 {
298 ExtOut ("Root %p promoted multiple times in gc %d\n",
299 SOS_PTR(Iter->Root),
300 GCCount);
301 bErrorFound = TRUE;
302 }
303 innerIter = innerIter->next;
304 }
305
306 Iter = Iter->next;
307 }
308 }
309
310 { // Relocates
311 RelocRecord *Iter = g_records[i].RelocList;
312 UINT GCCount = (UINT) g_records[i].GCCount;
313 while (Iter)
314 {
315 RelocRecord *innerIter = Iter->next;
316 while (innerIter)
317 {
318 if (Iter->Root == innerIter->Root)
319 {
320 ExtOut ("Root %p relocated multiple times in gc %d\n",
321 SOS_PTR(Iter->Root),
322 GCCount);
323 bErrorFound = TRUE;
324 }
325 innerIter = innerIter->next;
326 }
327
328 Iter = Iter->next;
329 }
330 }
331 }
332
333 if (!bErrorFound)
334 {
335 ExtOut ("No duplicate promote or relocate messages found in the log.\n");
336 }
337
338 return Status;
339}
340
341DECLARE_API(HistRoot)
342{
343 INIT_API();
344 size_t nArg;
345
346 StringHolder rootstr;
347 CMDValue arg[] =
348 {
349 // vptr, type
350 {&rootstr.data, COSTRING},
351 };
352
353 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
354 return Status;
355
356 if (nArg != 1)
357 {
358 ExtOut ("!Root <valid object pointer>\n");
359 return Status;
360 }
361
362 size_t Root = (size_t) GetExpression(rootstr.data);
363
364 ExtOut ("%8s %" POINTERSIZE "s %" POINTERSIZE "s %9s %20s\n",
365 "GCCount", "Value", "MT", "Promoted?", "Notes");
366 ExtOut ("---------------------------------------------------------\n");
367
368 bool bBoringPeople = false;
369
370 // Just traverse the data structure, printing basic stats
371 for (UINT i=0; i < g_recordCount; i++)
372 {
373 UINT GCCount = (UINT) g_records[i].GCCount;
374
375 // Find promotion records...there should only be one.
376 PromoteRecord *pPtr = g_records[i].PromoteList;
377 PromoteRecord *pPromoteRec = NULL;
378 bool bPromotedMoreThanOnce = false;
379 while(pPtr)
380 {
381 if (pPtr->Root == Root)
382 {
383 if (pPromoteRec)
384 {
385 bPromotedMoreThanOnce = true;
386 }
387 else
388 {
389 pPromoteRec = pPtr;
390 }
391 }
392 pPtr = pPtr->next;
393 }
394
395 RelocRecord *pReloc = g_records[i].RelocList;
396 RelocRecord *pRelocRec = NULL;
397 bool bRelocatedMoreThanOnce = false;
398 while(pReloc)
399 {
400 if (pReloc->Root == Root)
401 {
402 if (pRelocRec)
403 {
404 bRelocatedMoreThanOnce = true;
405 }
406 else
407 {
408 pRelocRec = pReloc;
409 }
410 }
411 pReloc = pReloc->next;
412 }
413
414 // Validate the records found for this root.
415 if (pRelocRec != NULL)
416 {
417 bBoringPeople = false;
418
419 ExtOut ("%8d %p %p %9s ", GCCount,
420 SOS_PTR(pRelocRec->NewValue),
421 SOS_PTR(pRelocRec->methodTable),
422 pPromoteRec ? "yes" : "no");
423 if (pPromoteRec != NULL)
424 {
425 // There should be similarities between the promote and reloc record
426 if (pPromoteRec->Value != pRelocRec->PrevValue ||
427 pPromoteRec->methodTable != pRelocRec->methodTable)
428 {
429 ExtOut ("promote/reloc records in error ");
430 }
431
432 if (bPromotedMoreThanOnce || bRelocatedMoreThanOnce)
433 {
434 ExtOut ("Duplicate promote/relocs");
435 }
436 }
437 ExtOut ("\n");
438 }
439 else if (pPromoteRec)
440 {
441 ExtOut ("Error: There is a promote record for root %p, but no relocation record\n",
442 (ULONG64) pPromoteRec->Root);
443 }
444 else
445 {
446 if (!bBoringPeople)
447 {
448 ExtOut ("...\n");
449 bBoringPeople = true;
450 }
451 }
452 }
453 return Status;
454}
455
456DECLARE_API(HistObjFind)
457{
458 INIT_API();
459 size_t nArg;
460
461 StringHolder objstr;
462 CMDValue arg[] =
463 {
464 // vptr, type
465 {&objstr.data, COSTRING},
466 };
467
468 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
469 return Status;
470
471 if (nArg != 1)
472 {
473 ExtOut ("!ObjSearch <valid object pointer>\n");
474 return Status;
475 }
476
477 size_t object = (size_t) GetExpression(objstr.data);
478
479 ExtOut ("%8s %" POINTERSIZE "s %40s\n",
480 "GCCount", "Object", "Message");
481 ExtOut ("---------------------------------------------------------\n");
482
483 size_t curAddress = object;
484 bool bBoringPeople = false;
485
486 // Just traverse the data structure, printing basic stats
487 for (UINT i=0; i < g_recordCount; i++)
488 {
489 if (curAddress == 0)
490 {
491 break;
492 }
493
494 UINT GCCount = (UINT) g_records[i].GCCount;
495
496 PromoteRecord *pPtr = g_records[i].PromoteList;
497 while(pPtr)
498 {
499 if (pPtr->Value == curAddress)
500 {
501 bBoringPeople = false;
502 ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
503 ExtOut ("Promotion for root %p (MT = %p)\n",
504 SOS_PTR(pPtr->Root),
505 SOS_PTR(pPtr->methodTable));
506 }
507 pPtr = pPtr->next;
508 }
509
510 RelocRecord *pReloc = g_records[i].RelocList;
511 while(pReloc)
512 {
513 if (pReloc->NewValue == curAddress ||
514 pReloc->PrevValue == curAddress)
515 {
516 bBoringPeople = false;
517 ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
518 ExtOut ("Relocation %s for root %p\n",
519 (pReloc->NewValue == curAddress) ? "NEWVALUE" : "PREVVALUE",
520 SOS_PTR(pReloc->Root));
521 }
522 pReloc = pReloc->next;
523 }
524
525 if (!bBoringPeople)
526 {
527 ExtOut ("...\n");
528 bBoringPeople = true;
529 }
530
531 }
532 return Status;
533}
534
535DECLARE_API(HistObj)
536{
537 INIT_API();
538 size_t nArg;
539
540 StringHolder objstr;
541 CMDValue arg[] =
542 {
543 // vptr, type
544 {&objstr.data, COSTRING},
545 };
546
547 if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
548 return Status;
549
550 if (nArg != 1)
551 {
552 ExtOut ("!object <valid object pointer>\n");
553 return Status;
554 }
555
556 size_t object = (size_t) GetExpression(objstr.data);
557
558 ExtOut ("%8s %" POINTERSIZE "s %40s\n",
559 "GCCount", "Object", "Roots");
560 ExtOut ("---------------------------------------------------------\n");
561
562 size_t curAddress = object;
563
564 // Just traverse the data structure, printing basic stats
565 for (UINT i=0; i < g_recordCount; i++)
566 {
567 if (curAddress == 0)
568 {
569 break;
570 }
571
572 UINT GCCount = (UINT) g_records[i].GCCount;
573
574 ExtOut ("%8d %p ", GCCount, SOS_PTR(curAddress));
575
576 RelocRecord *pReloc = g_records[i].RelocList;
577 size_t candidateCurAddress = curAddress;
578 bool bFirstReloc = true;
579 while(pReloc)
580 {
581 if (pReloc->NewValue == curAddress)
582 {
583 ExtOut ("%p, ", SOS_PTR(pReloc->Root));
584 if (bFirstReloc)
585 {
586 candidateCurAddress = pReloc->PrevValue;
587 bFirstReloc = false;
588 }
589 else if (candidateCurAddress != pReloc->PrevValue)
590 {
591 ExtOut ("differing reloc values for this object!\n");
592 }
593 }
594 pReloc = pReloc->next;
595 }
596
597 ExtOut ("\n");
598 curAddress = candidateCurAddress;
599 }
600 return Status;
601}
602
603DECLARE_API(HistInit)
604{
605 INIT_API();
606
607 GcHistClear();
608
609 CLRDATA_ADDRESS stressLogAddr = 0;
610 if (g_sos->GetStressLogAddress(&stressLogAddr) != S_OK)
611 {
612 ExtOut("Unable to find stress log via DAC\n");
613 return E_FAIL;
614 }
615
616 ExtOut ("Attempting to read Stress log\n");
617
618 Status = StressLog::Dump(stressLogAddr, NULL, g_ExtData);
619 if (Status == S_OK)
620 ExtOut("SUCCESS: GCHist structures initialized\n");
621 else if (Status == S_FALSE)
622 ExtOut("No Stress log in the image, GCHist commands unavailable\n");
623 else
624 ExtOut("FAILURE: Stress log unreadable\n");
625
626 return Status;
627}
628
629DECLARE_API(HistClear)
630{
631 INIT_API();
632 GcHistClear();
633 ExtOut("Completed successfully.\n");
634 return Status;
635}
636
637