Source code of Windows XP (NT5)
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.

665 lines
17 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. mergetl.c
  5. Abstract:
  6. Converts multiple ETL files into a single ordered ETL files.
  7. Author:
  8. Melur Raghuraman (Mraghu) 9-Dec-2000
  9. Revision History:
  10. --*/
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include <windows.h>
  17. #include <shellapi.h>
  18. #include <tchar.h>
  19. #include <wmistr.h>
  20. #include <objbase.h>
  21. #include <initguid.h>
  22. #include <wmium.h>
  23. #include <ntwmi.h>
  24. #include <wmiumkm.h>
  25. #include <evntrace.h>
  26. #include "cpdata.h"
  27. #include "tracectr.h"
  28. #define MAXSTR 1024
  29. #define LOGGER_NAME _T("{28ad2447-105b-4fe2-9599-e59b2aa9a634}")
  30. #define MAX_RETRY_COUNT 10
  31. #define ETW_PROC_MISMATCH 0x00000001
  32. #define ETW_MACHINE_MISMATCH 0x00000002
  33. #define ETW_CLOCK_MISMATCH 0x00000004
  34. #define ETW_BOOTTIME_MISMATCH 0x00000008
  35. #define ETW_VERSION_MISMATCH 0x00000010
  36. TRACEHANDLE LoggerHandle;
  37. ULONG TotalRelogBuffersRead = 0;
  38. ULONG TotalRelogEventsRead = 0;
  39. ULONG FailedEvents=0;
  40. GUID TransactionGuid =
  41. {0xab8bb8a1, 0x3d98, 0x430c, 0x92, 0xb0, 0x78, 0x8f, 0x1d, 0x3f, 0x6e, 0x94};
  42. GUID ControlGuid[2] =
  43. {
  44. {0x42ae6427, 0xb741, 0x4e69, 0xb3, 0x95, 0x38, 0x33, 0x9b, 0xb9, 0x91, 0x80},
  45. {0xb9e2c2d6, 0x95fb, 0x4841, 0xa3, 0x73, 0xad, 0x67, 0x2b, 0x67, 0xb6, 0xc1}
  46. };
  47. typedef struct _USER_MOF_EVENT {
  48. EVENT_TRACE_HEADER Header;
  49. MOF_FIELD mofData;
  50. } USER_MOF_EVENT, *PUSER_MOF_EVENT;
  51. typedef struct _ETW_RELOG_PROPERTIES {
  52. LONGLONG BootTime;
  53. LONGLONG PerfFreq;
  54. LONGLONG StartTime;
  55. LONGLONG StartPerfClock;
  56. LONGLONG EndTime;
  57. ULONG HeaderMisMatch;
  58. ULONG BuldNumber;
  59. ULONG MaxBufferSize;
  60. ULONG NumberOfProcessors;
  61. ULONG ClockType;
  62. ULONG TimerResolution;
  63. ULONG CpuspeedInMHz;
  64. ULONG ProviderVersion;
  65. ULONG TotalEventsLost;
  66. ULONG TotalBuffersLost;
  67. } ETW_RELOG_PROPERTIES, *PETW_RELOG_PROPERTIES;
  68. TRACE_GUID_REGISTRATION TraceGuidReg[] =
  69. {
  70. { (LPGUID)&TransactionGuid,
  71. NULL
  72. }
  73. };
  74. TRACEHANDLE RegistrationHandle[2];
  75. ULONG InitializeTrace(
  76. IN LPTSTR ExePath
  77. );
  78. ULONG
  79. ControlCallback(
  80. IN WMIDPREQUESTCODE RequestCode,
  81. IN PVOID Context,
  82. IN OUT ULONG *InOutBufferSize,
  83. IN OUT PVOID Buffer
  84. );
  85. void
  86. WINAPI
  87. EtwDumpEvent(
  88. PEVENT_TRACE pEvent
  89. );
  90. void
  91. WINAPI
  92. EtwProcessLogHeader(
  93. PEVENT_TRACE pEvent
  94. );
  95. ULONG
  96. WINAPI
  97. TerminateOnBufferCallback(
  98. PEVENT_TRACE_LOGFILE pLog
  99. );
  100. ULONG
  101. WINAPI
  102. BufferCallback(
  103. PEVENT_TRACE_LOGFILE pLog
  104. );
  105. USER_MOF_EVENT UserMofEvent;
  106. BOOLEAN bLoggerStarted = FALSE;
  107. PEVENT_TRACE_LOGFILE pLogFile=NULL;
  108. PEVENT_TRACE_LOGFILE EvmFile[MAXLOGGERS];
  109. ULONG LogFileCount = 0;
  110. PEVENT_TRACE_PROPERTIES pLoggerInfo = NULL;
  111. ULONG LoggerInfoSize = 0;
  112. ETW_RELOG_PROPERTIES EtwRelogProp;
  113. int EtwRelogEtl(
  114. PTRACE_CONTEXT_BLOCK TraceContext
  115. )
  116. {
  117. ULONG Status=ERROR_SUCCESS;
  118. ULONG i, j;
  119. TRACEHANDLE HandleArray[MAXLOGGERS];
  120. ULONG SizeNeeded = 0;
  121. LPTSTR LoggerName;
  122. LPTSTR LogFileName;
  123. TCHAR tstrLogFileName[MAXSTR];
  124. RtlZeroMemory(&EtwRelogProp, sizeof(ETW_RELOG_PROPERTIES) );
  125. RtlZeroMemory(tstrLogFileName, MAXSTR * sizeof(TCHAR));
  126. for (i = 0; i < TraceContext->LogFileCount; i++) {
  127. pLogFile = malloc(sizeof(EVENT_TRACE_LOGFILE));
  128. if (pLogFile == NULL) {
  129. Status = ERROR_OUTOFMEMORY;
  130. goto cleanup;
  131. }
  132. RtlZeroMemory(pLogFile, sizeof(EVENT_TRACE_LOGFILE));
  133. EvmFile[i] = pLogFile;
  134. pLogFile->LogFileName = TraceContext->LogFileName[i];
  135. EvmFile[i]->EventCallback = (PEVENT_CALLBACK) &EtwProcessLogHeader;
  136. EvmFile[i]->BufferCallback = TerminateOnBufferCallback;
  137. LogFileCount++;
  138. }
  139. if (LogFileCount == 0) {
  140. return ERROR_INVALID_PARAMETER;
  141. }
  142. //
  143. // Initialize Trace
  144. //
  145. Status = InitializeTrace(_T("tracerpt.exe"));
  146. if (Status != ERROR_SUCCESS) {
  147. return Status;
  148. }
  149. //
  150. // Set up the Relog Event
  151. //
  152. RtlZeroMemory(&UserMofEvent, sizeof(UserMofEvent));
  153. UserMofEvent.Header.Size = sizeof(UserMofEvent);
  154. UserMofEvent.Header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
  155. for (i = 0; i < LogFileCount; i++) {
  156. TRACEHANDLE x;
  157. EvmFile[i]->LogfileHeader.ReservedFlags |= EVENT_TRACE_GET_RAWEVENT;
  158. x = OpenTrace(EvmFile[i]);
  159. HandleArray[i] = x;
  160. if (HandleArray[i] == (TRACEHANDLE)INVALID_HANDLE_VALUE) {
  161. Status = GetLastError();
  162. for (j = 0; j < i; j++)
  163. CloseTrace(HandleArray[j]);
  164. goto cleanup;
  165. }
  166. Status = ProcessTrace(&x, 1, NULL, NULL);
  167. }
  168. for (j = 0; j < LogFileCount; j++){
  169. Status = CloseTrace(HandleArray[j]);
  170. }
  171. if (EtwRelogProp.HeaderMisMatch) {
  172. if (EtwRelogProp.HeaderMisMatch & ETW_CLOCK_MISMATCH)
  173. Status = ERROR_INVALID_TIME;
  174. else if (EtwRelogProp.HeaderMisMatch & ETW_PROC_MISMATCH)
  175. Status = ERROR_INVALID_DATA;
  176. goto cleanup;
  177. }
  178. if ( (EtwRelogProp.MaxBufferSize == 0) ||
  179. (EtwRelogProp.NumberOfProcessors == 0) ) {
  180. goto cleanup;
  181. }
  182. //
  183. // We are past the Error checks. Go ahead and Allocate
  184. // Storage to Start a logger
  185. //
  186. SizeNeeded = sizeof(EVENT_TRACE_PROPERTIES) + 2 * MAXSTR * sizeof(TCHAR) + LoggerInfoSize;
  187. pLoggerInfo = (PEVENT_TRACE_PROPERTIES) malloc(SizeNeeded);
  188. if (pLoggerInfo == NULL) {
  189. return(ERROR_OUTOFMEMORY);
  190. }
  191. RtlZeroMemory(pLoggerInfo, SizeNeeded);
  192. pLoggerInfo->Wnode.BufferSize = SizeNeeded;
  193. pLoggerInfo->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
  194. //
  195. // The relogged file contains a standard time stamp format.
  196. //
  197. pLoggerInfo->Wnode.ClientContext = EtwRelogProp.ClockType;
  198. pLoggerInfo->Wnode.ProviderId = EtwRelogProp.NumberOfProcessors;
  199. pLoggerInfo->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + LoggerInfoSize;
  200. pLoggerInfo->LogFileNameOffset = pLoggerInfo->LoggerNameOffset + MAXSTR * sizeof(TCHAR);
  201. pLoggerInfo->LogFileMode = (EVENT_TRACE_PRIVATE_LOGGER_MODE |
  202. EVENT_TRACE_RELOG_MODE |
  203. EVENT_TRACE_FILE_MODE_SEQUENTIAL
  204. );
  205. pLoggerInfo->BufferSize = EtwRelogProp.MaxBufferSize / 1024;
  206. pLoggerInfo->MinimumBuffers = 2;
  207. pLoggerInfo->MaximumBuffers = 50;
  208. LoggerName = (LPTSTR)((char*)pLoggerInfo + pLoggerInfo->LoggerNameOffset);
  209. LogFileName = (LPTSTR)((char*)pLoggerInfo + pLoggerInfo->LogFileNameOffset);
  210. _tcscpy(LoggerName, LOGGER_NAME );
  211. pLoggerInfo->Wnode.Guid = ControlGuid[0];
  212. if (_tcslen(TraceContext->MergeFileName) ) {
  213. _tcscpy(LogFileName, TraceContext->MergeFileName);
  214. }
  215. //
  216. // We are Past the Error Checks. Go ahead and redo ProcessTrace
  217. //
  218. for (i = 0; i < TraceContext->LogFileCount; i++) {
  219. TRACEHANDLE x;
  220. EvmFile[i]->EventCallback = (PEVENT_CALLBACK) &EtwDumpEvent;
  221. EvmFile[i]->BufferCallback = BufferCallback;
  222. EvmFile[i]->LogfileHeader.ReservedFlags |= EVENT_TRACE_GET_RAWEVENT;
  223. x = OpenTrace(EvmFile[i]);
  224. HandleArray[i] = x;
  225. if (HandleArray[i] == 0) {
  226. Status = GetLastError();
  227. for (j = 0; j < i; j++)
  228. CloseTrace(HandleArray[j]);
  229. goto cleanup;
  230. }
  231. }
  232. Status = ProcessTrace(
  233. HandleArray,
  234. LogFileCount,
  235. NULL,
  236. NULL
  237. );
  238. for (j = 0; j < LogFileCount; j++){
  239. Status = CloseTrace(HandleArray[j]);
  240. }
  241. //
  242. // Need to Stop Trace
  243. //
  244. if (bLoggerStarted)
  245. RtlZeroMemory(pLoggerInfo, SizeNeeded);
  246. pLoggerInfo->Wnode.BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + 2 * MAXSTR * sizeof(TCHAR);
  247. pLoggerInfo->Wnode.Guid = ControlGuid[0];
  248. pLoggerInfo->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
  249. pLoggerInfo->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
  250. pLoggerInfo->LogFileNameOffset = pLoggerInfo->LoggerNameOffset + MAXSTR * sizeof(TCHAR);
  251. pLoggerInfo->LogFileMode = (EVENT_TRACE_PRIVATE_LOGGER_MODE |
  252. EVENT_TRACE_RELOG_MODE |
  253. EVENT_TRACE_FILE_MODE_SEQUENTIAL
  254. );
  255. Status = StopTrace(LoggerHandle, LoggerName, pLoggerInfo);
  256. cleanup:
  257. for (i = 0; i < LogFileCount; i ++){
  258. free(EvmFile[i]);
  259. }
  260. RtlZeroMemory( &EtwRelogProp, sizeof(ETW_RELOG_PROPERTIES) );
  261. return Status;
  262. }
  263. void
  264. WINAPI
  265. EtwProcessLogHeader(
  266. PEVENT_TRACE pEvent
  267. )
  268. /*++
  269. Routine Description:
  270. This routine checks to see if the pEvent is a LOGFILE_HEADER
  271. and if so captures the information on the logfile for validation.
  272. The following checks are performed.
  273. 1. Files must be from the same machine. (Verified using machine name)
  274. 2. If different buffersizes are used, largest buffer size is
  275. selected for relogging.
  276. 3. The StartTime and StopTime are the outer most from the files.
  277. 4. The CPUClock type must be the same for all files. If different
  278. clock types are used, then the files will be rejected.
  279. The routine assumes that the first Event Callback from each file is the
  280. LogFileHeader callback.
  281. Other Issues that could result in a not so useful merged logfile are:
  282. 1. Multiple RunDown records when kernel logfiles are merged.
  283. 2. Multiple HWConfig records when kernel logfiles are merged
  284. 3. Multiple and conflicting GUidMap records when Application logfiles are merged.
  285. 4. ReLogging 32 bit data in 64 bit system
  286. Arguments:
  287. Return Value:
  288. None.
  289. --*/
  290. {
  291. ULONG NumProc;
  292. if( IsEqualGUID(&pEvent->Header.Guid, &EventTraceGuid) &&
  293. pEvent->Header.Class.Type == EVENT_TRACE_TYPE_INFO ) {
  294. PSYSTEM_TRACE_HEADER pSysHeader;
  295. PTRACE_LOGFILE_HEADER head = (PTRACE_LOGFILE_HEADER)((PUCHAR)pEvent->MofData + sizeof(SYSTEM_TRACE_HEADER) );
  296. ULONG BufferSize = head->BufferSize;
  297. pSysHeader = (PSYSTEM_TRACE_HEADER) pEvent->MofData;
  298. //
  299. // Pick up the First LogFileHeader's size
  300. //
  301. if (LoggerInfoSize == 0) {
  302. LPWSTR loggerName;
  303. LoggerInfoSize = pSysHeader->Packet.Size;
  304. loggerName = (LPWSTR) ( (char*)pEvent->MofData + sizeof(SYSTEM_TRACE_HEADER) +
  305. sizeof(TRACE_LOGFILE_HEADER) );
  306. }
  307. if (LoggerInfoSize < pSysHeader->Packet.Size) {
  308. LoggerInfoSize = pSysHeader->Packet.Size;
  309. }
  310. //
  311. // Pick up the Largest BufferSize
  312. //
  313. if (BufferSize > EtwRelogProp.MaxBufferSize) {
  314. EtwRelogProp.MaxBufferSize = BufferSize;
  315. }
  316. //
  317. // Verify the NumberOfProcessors
  318. //
  319. NumProc = head->NumberOfProcessors;
  320. if (EtwRelogProp.NumberOfProcessors) {
  321. if (EtwRelogProp.NumberOfProcessors != NumProc) {
  322. EtwRelogProp.HeaderMisMatch |= ETW_PROC_MISMATCH;
  323. }
  324. }
  325. else {
  326. EtwRelogProp.NumberOfProcessors = NumProc;
  327. }
  328. //
  329. // Pick up the Earliest StartTime
  330. //
  331. if (EtwRelogProp.StartTime) {
  332. if (head->StartTime.QuadPart < EtwRelogProp.StartTime) {
  333. EtwRelogProp.StartTime = head->StartTime.QuadPart;
  334. }
  335. }
  336. else {
  337. EtwRelogProp.StartTime = head->StartTime.QuadPart;
  338. }
  339. //
  340. // Pick up the latest EndTime
  341. //
  342. if (EtwRelogProp.EndTime) {
  343. if (head->EndTime.QuadPart > EtwRelogProp.EndTime) {
  344. EtwRelogProp.EndTime = head->EndTime.QuadPart;
  345. }
  346. }
  347. else {
  348. EtwRelogProp.EndTime = head->EndTime.QuadPart;
  349. }
  350. if (EtwRelogProp.StartPerfClock) {
  351. PSYSTEM_TRACE_HEADER pSysHeader = (PSYSTEM_TRACE_HEADER) ((PUCHAR)pEvent->MofData);
  352. if (pSysHeader->SystemTime.QuadPart < EtwRelogProp.StartPerfClock) {
  353. EtwRelogProp.StartPerfClock = pSysHeader->SystemTime.QuadPart;
  354. }
  355. }
  356. else {
  357. PSYSTEM_TRACE_HEADER pSysHeader = (PSYSTEM_TRACE_HEADER) ((PUCHAR)pEvent->MofData);
  358. EtwRelogProp.StartPerfClock = pSysHeader->SystemTime.QuadPart;
  359. }
  360. //
  361. // Verify the Clock Type
  362. //
  363. if (EtwRelogProp.ClockType) {
  364. if (head->ReservedFlags != EtwRelogProp.ClockType) {
  365. EtwRelogProp.HeaderMisMatch |= ETW_CLOCK_MISMATCH;
  366. }
  367. }
  368. else {
  369. EtwRelogProp.ClockType = head->ReservedFlags;
  370. }
  371. if (EtwRelogProp.PerfFreq) {
  372. if (EtwRelogProp.PerfFreq != head->PerfFreq.QuadPart) {
  373. EtwRelogProp.HeaderMisMatch |= ETW_MACHINE_MISMATCH;
  374. }
  375. }
  376. else {
  377. EtwRelogProp.PerfFreq = head->PerfFreq.QuadPart;
  378. }
  379. //
  380. // Verify Machine Name
  381. //
  382. // CPU Name is in the CPU Configuration record
  383. // which can be version dependent and found only on Kernel Logger
  384. //
  385. // Verify Build Number
  386. //
  387. if (EtwRelogProp.ProviderVersion) {
  388. if (EtwRelogProp.ProviderVersion != head->ProviderVersion) {
  389. EtwRelogProp.HeaderMisMatch |= ETW_VERSION_MISMATCH;
  390. }
  391. }
  392. else {
  393. EtwRelogProp.ProviderVersion = head->ProviderVersion;
  394. }
  395. //
  396. // Boot Time Verification?
  397. //
  398. if (EtwRelogProp.BootTime) {
  399. if (EtwRelogProp.BootTime != head->BootTime.QuadPart) {
  400. EtwRelogProp.HeaderMisMatch |= ETW_BOOTTIME_MISMATCH;
  401. }
  402. }
  403. else {
  404. EtwRelogProp.BootTime = head->BootTime.QuadPart;
  405. }
  406. //
  407. // Sum up Events Lost from each file
  408. //
  409. EtwRelogProp.TotalEventsLost += head->EventsLost;
  410. EtwRelogProp.TotalBuffersLost += head->BuffersLost;
  411. }
  412. }
  413. void
  414. WINAPI
  415. EtwDumpEvent(
  416. PEVENT_TRACE pEvent
  417. )
  418. {
  419. PEVENT_TRACE_HEADER pHeader;
  420. ULONG Status = ERROR_SUCCESS;
  421. ULONG CachedFlags;
  422. USHORT CachedSize;
  423. ULONG RetryCount = 0;
  424. if (pEvent == NULL) {
  425. return;
  426. }
  427. TotalRelogEventsRead++;
  428. if (!bLoggerStarted) {
  429. PSYSTEM_TRACE_HEADER pSysHeader;
  430. ULONG CachedBufferSize = 0;
  431. PSYSTEM_TRACE_HEADER pTarget;
  432. PTRACE_LOGFILE_HEADER pLogFileHeader;
  433. LPWSTR loggerName;
  434. // Assume that this first Event is the LogFileHeader and validate its size
  435. pSysHeader = (PSYSTEM_TRACE_HEADER) pEvent->MofData;
  436. pTarget = (PSYSTEM_TRACE_HEADER) ((PUCHAR)pLoggerInfo +
  437. sizeof(EVENT_TRACE_PROPERTIES) );
  438. pLogFileHeader = (PTRACE_LOGFILE_HEADER) ((PUCHAR)pTarget +
  439. sizeof(SYSTEM_TRACE_HEADER) );
  440. loggerName = (LPWSTR) ( (char*)pEvent->MofData + sizeof(SYSTEM_TRACE_HEADER) +
  441. sizeof(TRACE_LOGFILE_HEADER) );
  442. if (LoggerInfoSize < pSysHeader->Packet.Size) {
  443. exit(-1);
  444. }
  445. CachedBufferSize = pLogFileHeader->BufferSize;
  446. pLogFileHeader->BufferSize = EtwRelogProp.MaxBufferSize;
  447. pLogFileHeader->EventsLost = EtwRelogProp.TotalEventsLost;
  448. pLogFileHeader->BuffersLost = EtwRelogProp.TotalBuffersLost;
  449. pLogFileHeader->EndTime.QuadPart = EtwRelogProp.EndTime;
  450. pLogFileHeader->StartTime.QuadPart = EtwRelogProp.StartTime;
  451. RtlCopyMemory(pTarget, pSysHeader, LoggerInfoSize);
  452. Status = StartTrace(&LoggerHandle, LOGGER_NAME, pLoggerInfo);
  453. pLogFileHeader->BufferSize = CachedBufferSize;
  454. if (Status != ERROR_SUCCESS) {
  455. return;
  456. }
  457. bLoggerStarted = TRUE;
  458. return;
  459. }
  460. pHeader = (PEVENT_TRACE_HEADER)pEvent->MofData;
  461. CachedSize = pEvent->Header.Size;
  462. CachedFlags = pEvent->Header.Flags;
  463. pEvent->Header.Size = sizeof(EVENT_TRACE);
  464. pEvent->Header.Flags |= (WNODE_FLAG_TRACED_GUID | WNODE_FLAG_NO_HEADER);
  465. do {
  466. Status = TraceEvent(LoggerHandle, (PEVENT_TRACE_HEADER)pEvent );
  467. if ( ( Status == ERROR_OUTOFMEMORY) && (RetryCount++ < MAX_RETRY_COUNT) ) {
  468. _sleep(500); // Sleep for half a second.
  469. }
  470. else {
  471. break;
  472. }
  473. } while (TRUE);
  474. if (Status != ERROR_SUCCESS) {
  475. FailedEvents++;
  476. }
  477. //
  478. // Restore Cached values
  479. //
  480. pEvent->Header.Size = CachedSize;
  481. pEvent->Header.Flags = CachedFlags;
  482. }
  483. ULONG InitializeTrace(
  484. IN LPTSTR ExePath
  485. )
  486. {
  487. ULONG Status;
  488. Status = RegisterTraceGuids(
  489. (WMIDPREQUEST)ControlCallback,
  490. NULL,
  491. (LPCGUID)&ControlGuid[0],
  492. 1,
  493. &TraceGuidReg[0],
  494. NULL,
  495. NULL,
  496. &RegistrationHandle[0]
  497. );
  498. return(Status);
  499. }
  500. ULONG
  501. ControlCallback(
  502. IN WMIDPREQUESTCODE RequestCode,
  503. IN PVOID Context,
  504. IN OUT ULONG *InOutBufferSize,
  505. IN OUT PVOID Buffer
  506. )
  507. {
  508. return ERROR_SUCCESS;
  509. }
  510. ULONG
  511. WINAPI
  512. TerminateOnBufferCallback(
  513. PEVENT_TRACE_LOGFILE pLog
  514. )
  515. {
  516. return (FALSE); // Terminate ProcessTrace on First BufferCallback
  517. }
  518. ULONG
  519. WINAPI
  520. BufferCallback(
  521. PEVENT_TRACE_LOGFILE pLog
  522. )
  523. {
  524. TotalRelogBuffersRead++;
  525. return (TRUE);
  526. }