Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

487 lines
20 KiB

  1. // WppFmt.c
  2. // This module contains the routines used by BinPlace to copy out the trace format information used
  3. // by software tracing. It creates "guid".tmf files
  4. // The pre-processor TraceWpp creates annotation records in the PDB with the first string bwinfg the text
  5. // "TMF:" we find these annotation records and extract the complete record. The first record
  6. // after "TMF:" contains the guid and friendly name. This GUID is used to create the filename.
  7. // Currently the remainder of the records are copied to the file, a possible future change is to turn the
  8. // file into a pointer file.
  9. // Based on PDB sample code from VC folks, with names kept the same.
  10. #ifdef __cplusplus
  11. extern "C"{
  12. #endif
  13. //#define UNICODE
  14. //#define _UNICODE
  15. #define FUNCNAME_MAX_SIZE 256
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <nt.h>
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include <windows.h>
  22. #include <shellapi.h>
  23. #include <tchar.h>
  24. #include <dbghelp.h>
  25. #include <cvinfo.h>
  26. #define PDB_LIBRARY
  27. #include <pdb.h>
  28. #include <strsafe.h>
  29. typedef LONG CB; // count of bytes
  30. typedef CHAR * ST;
  31. typedef SYMTYPE* PSYM;
  32. typedef SYMTYPE UNALIGNED * PSYMUNALIGNED;
  33. typedef BYTE* PB; // pointer to some bytes
  34. FILE* TraceFileP = NULL; // Current File
  35. CHAR lastguid[MAX_PATH]; // The last file we processed
  36. CHAR TraceFile[MAX_PATH]; // The current full file spec.
  37. CHAR TraceFileExt[] = ".tmf" ; // Extension used by Trace Files
  38. CHAR TraceControlExt[] = ".tmc" ; // Extension used by Trace Control Files
  39. BOOL TracePathChecked = FALSE ; // if we have ascertained the trace Path exists.
  40. CHAR Fname[MAX_PATH] ;
  41. CHAR Mname[MAX_PATH] ;
  42. #define GUIDTEXTLENGTH 32+4 // Guid takes 32 chars plus 4 -'s
  43. #define MAXLINE MAX_PATH + 256
  44. CHAR Line[MAXLINE] ;
  45. CHAR Line2[MAXLINE] ;
  46. CHAR FirstLine[MAXLINE] ;
  47. CHAR SecondLine[MAXLINE] ;
  48. BOOL fVerbose = FALSE ;
  49. typedef BOOL ( __cdecl *PPDBOPEN )(
  50. LNGNM_CONST char *,
  51. LNGNM_CONST char *,
  52. SIG,
  53. EC *,
  54. char [cbErrMax],
  55. PDB **
  56. );
  57. typedef BOOL ( __cdecl *PPDBCLOSE ) (
  58. PDB* ppdb
  59. );
  60. typedef BOOL ( __cdecl *PPDBOPENDBI ) (
  61. PDB* ppdb, const char* szMode, const char* szTarget, OUT DBI** ppdbi
  62. );
  63. typedef BOOL ( __cdecl *PDBICLOSE ) (
  64. DBI* pdbi
  65. );
  66. typedef BOOL ( __cdecl *PMODQUERYSYMBOLS ) (
  67. Mod* pmod, BYTE* pbSym, long* pcb
  68. );
  69. typedef BOOL ( __cdecl *PDBIQUERYNEXTMOD ) (
  70. DBI* pdbi, Mod* pmod, Mod** ppmodNext
  71. );
  72. static PPDBOPEN pPDBOpen = NULL;
  73. static PPDBCLOSE pPDBClose = NULL;
  74. static PPDBOPENDBI pPDBOpenDBI = NULL;
  75. static PDBICLOSE pDBIClose = NULL;
  76. static PMODQUERYSYMBOLS pModQuerySymbols = NULL;
  77. static PDBIQUERYNEXTMOD pDBIQueryNextMod = NULL;
  78. static BOOL RSDSLibLoaded = FALSE;
  79. // Return the number of bytes the symbol record occupies.
  80. #define MDALIGNTYPE_ DWORD
  81. __inline CB cbAlign_(CB cb) {
  82. return((cb + sizeof(MDALIGNTYPE_) - 1)) & ~(sizeof(MDALIGNTYPE_) - 1);}
  83. // Return the number of bytes in an ST
  84. __inline CB cbForSt(ST st) { return *(PB)st + 1;}
  85. CB cbForSym(PSYMUNALIGNED psym)
  86. {
  87. CB cb = psym->reclen + sizeof(psym->reclen);
  88. // procrefs also have a hidden length preceeded name following the record
  89. if ((psym->rectyp == S_PROCREF) || (psym->rectyp == S_LPROCREF))
  90. cb += cbAlign_(cbForSt((ST)(psym + cb)));
  91. return cb;
  92. }
  93. // Return a pointer to the byte just past the end of the symbol record.
  94. PSYM pbEndSym(PSYM psym) { return(PSYM)((CHAR *)psym + cbForSym(psym));}
  95. CHAR * SymBuffer = 0;
  96. int SymBufferSize = 0;
  97. BOOL ensureBufferSizeAtLeast(int size)
  98. {
  99. if (size > SymBufferSize) {
  100. LocalFree(SymBuffer);
  101. SymBufferSize = 0;
  102. size = (size + 0xFFFF) & ~0xFFFF;
  103. SymBuffer = LocalAlloc(LMEM_FIXED, size );
  104. if (!SymBuffer) {
  105. fprintf(stderr,"%s : error BNP0000: WPPFMT alloc of %d bytes failed\n",Fname, size);
  106. return FALSE;
  107. }
  108. SymBufferSize = size;
  109. }
  110. return TRUE;
  111. }
  112. void dumpSymbol(PSYM psym,
  113. PSTR PdbFileName,
  114. PSTR TraceFormatFilePath)
  115. {
  116. static char FuncName[FUNCNAME_MAX_SIZE +1] = "Unknown";
  117. static char Fmode[8] = "w" ;
  118. HRESULT hRtrn ;
  119. if (psym->rectyp == S_GPROC32 || psym->rectyp == S_LPROC32) {
  120. PROCSYM32* p = (PROCSYM32*)psym;
  121. int n = p->name[0];
  122. if (n > FUNCNAME_MAX_SIZE) {
  123. FuncName[0] = 0 ; // ignore too long, Illegal, name.
  124. } else {
  125. memcpy(FuncName, p->name + 1, n);
  126. FuncName[n] = 0;
  127. }
  128. return;
  129. }
  130. //
  131. // The following is a complete crock to let us handle some V7 PDB changes
  132. // This code will have qbe changed by DIA but this lets users get work done
  133. #define S_GPROC32_V7 0x110f
  134. #define S_LPROC32_V7 0x1110
  135. if (psym->rectyp == (S_GPROC32_V7) || psym->rectyp == (S_LPROC32_V7)) {
  136. PROCSYM32* p = (PROCSYM32*)psym;
  137. strncpy(FuncName,p->name, 256); // Note name is null terminated, not length!!!
  138. return;
  139. }
  140. // End of V7 PDB crock
  141. if (psym->rectyp == S_ANNOTATION) {
  142. ANNOTATIONSYM* aRec = (ANNOTATIONSYM*) psym;
  143. UCHAR * Aline = aRec->rgsz;
  144. int cnt = aRec->csz, i;
  145. CHAR* Ext;
  146. if ( cnt < 2 ) {
  147. return;
  148. }
  149. if ( strcmp(Aline, "TMF:") == 0 ) {
  150. Ext = TraceFileExt;
  151. } else if ( strcmp(Aline, "TMC:") == 0 ) {
  152. Ext = TraceControlExt;
  153. } else {
  154. return;
  155. }
  156. // skip tmf
  157. Aline += strlen(Aline) + 1;
  158. // now Aline points to guid, is it the same as before?
  159. if ( (TraceFileP != stdout) && strncmp(Aline, lastguid, GUIDTEXTLENGTH) != 0) {
  160. // the guid has changed, we need to change the file
  161. if (TraceFileP) {
  162. fclose(TraceFileP); // Close the last one
  163. TraceFileP = NULL ;
  164. }
  165. if (GUIDTEXTLENGTH < sizeof(lastguid) ) {
  166. strncpy(lastguid, Aline, GUIDTEXTLENGTH);
  167. } else {
  168. fprintf(stderr,"%s : error BNP0000: WPPFMT GUID buffer too small \n",Fname);
  169. return; // then game over
  170. }
  171. if (StringCchPrintf(TraceFile,MAX_PATH,"%s\\%s%s",TraceFormatFilePath,lastguid,Ext) == STRSAFE_E_INSUFFICIENT_BUFFER ) {
  172. fprintf(stderr,"%s : error BNP0000: WPPFMT File + Path too long %s\n",Fname, TraceFile);
  173. return; // then game over.
  174. }
  175. if (!TracePathChecked) {
  176. if (!MakeSureDirectoryPathExists(TraceFile)) { // Make the directory if we need to
  177. fprintf(stderr,"%s : error BNP0000: WPPFMT Failed to make path %s\n",Fname, TraceFile);
  178. return;
  179. } else {
  180. TracePathChecked = TRUE ;
  181. }
  182. }
  183. // In this case we have to deal with situation where we come across the same GUID but its
  184. // from a different PDB. This can validly happen when say a library is trace enabled and
  185. // included with multiple components, they each have part of the trace information, though
  186. // now it belongs to the PDB of the calling process.
  187. // So we have the PDB name in the first line, and the date on the second and the logic is
  188. // If PDB names do not match then APPEND
  189. // If PDB names and date match then APPEND
  190. // If name matches but date does not then Overwrite.
  191. //
  192. hRtrn = StringCchPrintf(Fmode, 7, "w"); // Assume its to be overwritten
  193. if ((TraceFileP = fopen(TraceFile,"r")) != NULL ) {
  194. // Hmm it already exists, is it an old one or must we add to it.
  195. if (_fgetts(Line, MAXLINE, TraceFileP)) { // Get the First line
  196. if (strncmp(Line,FirstLine,MAXLINE) == 0) { // Is it us?
  197. if (_fgetts(Line2, MAXLINE, TraceFileP)) { // Get the second line
  198. if (strncmp(Line2,SecondLine,MAXLINE) == 0) { // Really us for this build?
  199. hRtrn = StringCchPrintf(Fmode, 7, "a"); // Yes Append
  200. }
  201. }
  202. } else {
  203. hRtrn = StringCchPrintf(Fmode, 7, "a"); // Not us, most likely a library so Append
  204. }
  205. }
  206. fclose (TraceFileP);
  207. TraceFileP = NULL ;
  208. }
  209. TraceFileP = fopen(TraceFile, Fmode);
  210. if (!TraceFileP) {
  211. if (fVerbose) {
  212. fprintf(stderr,"%s : warning BNP0000: WPPFMT Failed to open %s (0x%0X)\n",Fname, TraceFile,GetLastError());
  213. }
  214. return;
  215. }
  216. if (fVerbose) {
  217. fprintf(stdout,"%s : info BNP0000: WPPFMT generating %s for %s\n",
  218. Fname, TraceFile, PdbFileName);
  219. }
  220. if (!(strcmp(Fmode,"w"))) {
  221. // First time around comment on what we are doing.
  222. fprintf(TraceFileP,"%s",FirstLine); // Note the name of the PDB etc.
  223. fprintf(TraceFileP,"%s",SecondLine);
  224. fprintf(TraceFileP,"%s\n",Aline); // Guid and friendly name
  225. } else {
  226. fprintf(TraceFileP,"%s",FirstLine);
  227. fprintf(TraceFileP,"%s",SecondLine);
  228. }
  229. }
  230. // process the annotation which is a series of null terminated strings.
  231. cnt -= 2; Aline += strlen(Aline) + 1;
  232. for (i = 0; i < cnt; ++i) {
  233. if (i == 0) {
  234. fprintf(TraceFileP, "%s FUNC=%s\n", Aline, FuncName);
  235. } else {
  236. fprintf(TraceFileP,"%s\n", Aline);
  237. }
  238. Aline += strlen(Aline) + 1;
  239. }
  240. }
  241. }
  242. DWORD
  243. BinplaceWppFmt(
  244. LPSTR PdbFileName,
  245. LPSTR TraceFormatFilePath,
  246. LPSTR szRSDSDllToLoad,
  247. BOOL TraceVerbose
  248. )
  249. {
  250. PDB *pPdb;
  251. DBI *pDbi;
  252. HANDLE hPdb ;
  253. UCHAR szErr[cbErrMax];
  254. EC errorCode;
  255. Mod* pMod;
  256. DWORD Status ;
  257. BOOL rc;
  258. fVerbose = TraceVerbose ;
  259. Line[0] = Line2[0] = FirstLine[0] = SecondLine[0] = 0 ; // Initiallise to be careful of changes.
  260. // get name of the caller
  261. if ((Status = GetModuleFileName(NULL, Mname, MAX_PATH)) == 0) {
  262. fprintf(stderr,"UNKNOWN : error BNP0000: WPPFMT GetModuleFileName Failed %08X\n",GetLastError());
  263. return(FALSE);
  264. }
  265. _splitpath(Mname, NULL, NULL, Fname, NULL);
  266. #ifdef _WIN64
  267. rc = FALSE;
  268. #else
  269. __try
  270. {
  271. if (hPdb = CreateFile(PdbFileName,
  272. GENERIC_READ,
  273. 0,NULL,
  274. OPEN_EXISTING,
  275. FILE_ATTRIBUTE_NORMAL,
  276. NULL)) {
  277. FILETIME ftime ;
  278. SYSTEMTIME stime ;
  279. if (GetFileTime(hPdb,NULL,NULL,&ftime)) {
  280. if ( FileTimeToSystemTime(&ftime,&stime) ) {
  281. if (StringCchPrintf(FirstLine,MAXLINE,"//PDB: %s\n",PdbFileName) == STRSAFE_E_INSUFFICIENT_BUFFER ) { // Ok deal with being too long, but we have a NULL
  282. FirstLine[MAXLINE-2] = '\n' ; // But make sure we have a newline
  283. }
  284. if (StringCchPrintf(SecondLine,MAXLINE,"//PDB: Last Updated :%d-%d-%d:%d:%d:%d:%d (UTC) [%s]\n",
  285. stime.wYear,stime.wMonth,stime.wDay,
  286. stime.wHour,stime.wMinute,stime.wSecond,stime.wMilliseconds,
  287. Fname) == STRSAFE_E_INSUFFICIENT_BUFFER ) { // Ok deal with being too long, but we have a NULL
  288. SecondLine[MAXLINE-2] = '\n' ; // But make sure we have a newline
  289. }
  290. }
  291. }
  292. CloseHandle(hPdb);
  293. } else {
  294. // Let the failure case be dealt with by PDBOpen
  295. }
  296. rc=PDBOpen(PdbFileName, pdbRead, 0, &errorCode, szErr, &pPdb);
  297. if ((rc != 0) && (errorCode == 0)) { // ignore bad return as we are replacing this anyway
  298. rc = 0 ;
  299. }
  300. }
  301. __except( EXCEPTION_EXECUTE_HANDLER )
  302. {
  303. rc=FALSE;
  304. }
  305. #endif
  306. if ( !rc ) {
  307. // Try the one that works with RSDS
  308. if ( szRSDSDllToLoad != NULL ) {
  309. HMODULE hDll;
  310. if ( !RSDSLibLoaded ) {
  311. hDll = LoadLibrary( szRSDSDllToLoad );
  312. if (hDll != NULL) {
  313. RSDSLibLoaded = TRUE;
  314. pPDBOpen = ( PPDBOPEN ) GetProcAddress( hDll, "PDBOpen" );
  315. if (pPDBOpen == NULL ) {
  316. fprintf(stderr,"%s : error BNP0000: WPPFMT GEtPRocAddressFailed PDBOpen\n",Fname);
  317. return(FALSE);
  318. }
  319. pPDBClose = ( PPDBCLOSE ) GetProcAddress( hDll, "PDBClose" );
  320. if (pPDBClose == NULL ) {
  321. fprintf(stderr,"%s : error BNP0000: WPPFMT GEtPRocAddressFailed PDBOClose\n",Fname);
  322. return(FALSE);
  323. }
  324. pPDBOpenDBI = ( PPDBOPENDBI ) GetProcAddress( hDll, "PDBOpenDBI" );
  325. if (pPDBOpenDBI == NULL ) {
  326. fprintf(stderr,"%s : error BNP0000: WPPFMT GEtPRocAddressFailed PDBOpenDBI\n",Fname);
  327. return(FALSE);
  328. }
  329. pDBIClose = ( PDBICLOSE ) GetProcAddress( hDll, "DBIClose" );
  330. if (pDBIClose == NULL ) {
  331. fprintf(stderr,"%s : error BNP0000: WPPFMT GEtPRocAddressFailed DBICLOSE\n",Fname);
  332. return(FALSE);
  333. }
  334. pDBIQueryNextMod = ( PDBIQUERYNEXTMOD ) GetProcAddress( hDll, "DBIQueryNextMod" );
  335. if (pDBIQueryNextMod == NULL ) {
  336. fprintf(stderr,"%s : error BNP0000: WPPFMT GEtPRocAddressFailed DBIQueryNextMod\n",Fname);
  337. return(FALSE);
  338. }
  339. pModQuerySymbols = ( PMODQUERYSYMBOLS ) GetProcAddress( hDll, "ModQuerySymbols" );
  340. if (pModQuerySymbols == NULL ) {
  341. fprintf(stderr,"%s : error BNP0000: WPPFMT GEtPRocAddressFailed ModQuerySymbols\n",Fname);
  342. return(FALSE);
  343. }
  344. } else {
  345. fprintf(stderr,"%s : error BNP0000: WPPFMT Failed to load library %s (0x%08X)\n",Fname, szRSDSDllToLoad,GetLastError());
  346. return(FALSE);
  347. }
  348. }
  349. }
  350. if (RSDSLibLoaded) {
  351. __try
  352. {
  353. rc = pPDBOpen(PdbFileName, pdbRead, 0, &errorCode, szErr, &pPdb);
  354. }
  355. __except (EXCEPTION_EXECUTE_HANDLER )
  356. {
  357. rc=FALSE;
  358. }
  359. }
  360. }
  361. if (!rc) {
  362. fprintf(stderr,"%s : warning BNP0000: WPPFMT PDBOpen failed, code %d, error %s\n",
  363. Fname, errorCode, szErr);
  364. goto fail1;
  365. }
  366. if (RSDSLibLoaded) {
  367. rc = pPDBOpenDBI(pPdb, pdbRead, "<target>.exe", &pDbi);
  368. } else {
  369. rc = PDBOpenDBI(pPdb, pdbRead, "<target>.exe", &pDbi);
  370. }
  371. if (!rc) {
  372. fprintf(stderr,"%s : warning BNP0000: WPPFMT PDBOpenDBI failed\n",Fname);
  373. goto fail2;
  374. }
  375. if (RSDSLibLoaded) {
  376. for (pMod = 0; pDBIQueryNextMod(pDbi, pMod, &pMod) && pMod; ) {
  377. CB cbSyms;
  378. if ( pModQuerySymbols(pMod, 0, &cbSyms)
  379. && ensureBufferSizeAtLeast(cbSyms)
  380. && pModQuerySymbols(pMod, SymBuffer, &cbSyms) ) {
  381. PSYM psymEnd = (PSYM)(SymBuffer + cbSyms);
  382. PSYM psym = (PSYM)(SymBuffer + sizeof(ULONG));
  383. for (; psym < psymEnd; psym = pbEndSym(psym))
  384. dumpSymbol(psym,PdbFileName,TraceFormatFilePath);
  385. } else {
  386. fprintf(stderr,"%s : warning BNP0000: WPPFMT ModQuerySymbols failed pMod = %p cbSyms = %d\n",
  387. Fname, pMod, cbSyms);
  388. break;
  389. }
  390. }
  391. } else {
  392. for (pMod = 0; DBIQueryNextMod(pDbi, pMod, &pMod) && pMod; ) {
  393. CB cbSyms;
  394. if ( ModQuerySymbols(pMod, 0, &cbSyms)
  395. && ensureBufferSizeAtLeast(cbSyms)
  396. && ModQuerySymbols(pMod, SymBuffer, &cbSyms) ) {
  397. PSYM psymEnd = (PSYM)(SymBuffer + cbSyms);
  398. PSYM psym = (PSYM)(SymBuffer + sizeof(ULONG));
  399. for (; psym < psymEnd; psym = pbEndSym(psym))
  400. dumpSymbol(psym,PdbFileName,TraceFormatFilePath);
  401. } else {
  402. fprintf(stderr,"%s : warning BNP0000: WPPFMT ModQuerySymbols failed pMod = %p cbSyms = %d\n",
  403. Fname, pMod, cbSyms);
  404. break;
  405. }
  406. }
  407. }
  408. if (RSDSLibLoaded) {
  409. pDBIClose(pDbi);
  410. } else {
  411. DBIClose(pDbi);
  412. }
  413. fail2:
  414. if (RSDSLibLoaded) {
  415. pPDBClose(pPdb);
  416. } else {
  417. PDBClose(pPdb);
  418. }
  419. fail1:
  420. if (TraceFileP) {
  421. fclose(TraceFileP); // Close the last one
  422. TraceFileP = NULL ;
  423. }
  424. return errorCode;
  425. }
  426. #ifdef __cplusplus
  427. }
  428. #endif