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.

1249 lines
28 KiB

  1. //+------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1993.
  5. //
  6. // File: procswap.cxx
  7. //
  8. // Contents: Program for measuring task switching performance
  9. // between two windows programs. The program creates
  10. // CTaskSwitch objects...
  11. //
  12. // Each Object can wait on one of three things...
  13. // 1. GetMessage
  14. // 2. MsgWaitForMultipleObjects
  15. // 3. WaitForSingleObject (event)
  16. //
  17. // and when awoken, will signal another Object in one
  18. // of three ways...
  19. // 1. PostMessage
  20. // 2. SendMessage
  21. // 3. SetEvent
  22. //
  23. // These cases can be combined in any manner to obtain
  24. // a maxtrix of possible scenarios.
  25. //
  26. // The CTaskSwitch objects can be in the same process on
  27. // different threads, or in different processes.
  28. //
  29. // Classes: CEvent - event handling class
  30. // CTaskSwitch - main task switch class
  31. //
  32. //
  33. // Functions: WinMain - entry point of process
  34. // ThreadEntry - entry point of spawned threads
  35. // ThreadWndProc - processes windows messages
  36. //
  37. //
  38. // History: 08-Feb-94 Rickhi Created
  39. //
  40. //--------------------------------------------------------------------------
  41. #include <benchmrk.hxx>
  42. #include <tchar.h>
  43. // execution parameter structure
  44. typedef struct tagSExecParms
  45. {
  46. int oloop; // outer loop count
  47. int iloop; // inner loop count
  48. HWND hWndOther; // HWND of other process
  49. HANDLE hEventOther; // Event Handle of other process
  50. WNDPROC pfnWndProc; // ptr to WndProc function
  51. TCHAR szFile[20]; // output file name
  52. TCHAR szWaitEvent[20]; // event name to wait on
  53. TCHAR szSignalEvent[20]; // event name to signal
  54. } SExecParms;
  55. typedef enum tagWAITTYPES
  56. {
  57. WAIT_EVENT = 1,
  58. WAIT_MSGWAITFORMULTIPLE = 2,
  59. WAIT_GETMESSAGE = 3,
  60. WAIT_SYNCHRONOUS = 4
  61. } WAITTYPES;
  62. typedef enum tagSIGNALTYPES
  63. {
  64. SIGNAL_EVENT = 1,
  65. SIGNAL_POSTMESSAGE = 2,
  66. SIGNAL_SENDMESSAGE = 3,
  67. SIGNAL_SYNCHRONOUS = 4
  68. } SIGNALTYPES;
  69. // input names corresponding to the wait types
  70. LPSTR aszWait[] = {"", "event", "msgwait", "getmsg", "sync", NULL};
  71. // input names corresponding to the signal types
  72. LPSTR aszSignal[] = {"", "event", "postmsg", "sendmsg", "sync", NULL};
  73. // Name of window class for dispatching messages.
  74. #define MY_WINDOW_CLASS TEXT("ProcSwapWindowClass")
  75. #define MAX_OLOOP 100
  76. // globals
  77. DWORD g_fFullInfo = 0; // write full info or not
  78. HINSTANCE g_hInst = NULL; // misc windows junk.
  79. ATOM g_MyClass = 0;
  80. UINT g_MyMessage = WM_USER;
  81. // function prototype
  82. LRESULT ThreadWndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam);
  83. DWORD ThreadEntry(void *param);
  84. //--------------------------------------------------------------------------
  85. //
  86. // Class: CEvent
  87. //
  88. // Purpose: class for blocking & starting threads.
  89. //
  90. //--------------------------------------------------------------------------
  91. class CEvent
  92. {
  93. public:
  94. CEvent(LPTSTR szName, HRESULT &hr) { Init(szName, hr); }
  95. CEvent(void) {m_hdl = NULL; }
  96. ~CEvent() { CloseHandle(m_hdl); }
  97. void Signal(void) { SetEvent(m_hdl); }
  98. void Reset(void) { ; } // ResetEvent(m_hdl); }
  99. void BlockS(void) { WaitForSingleObject(m_hdl, 60000); }
  100. void BlockM(void) { WaitForMultipleObjects(1, &m_hdl, FALSE, 60000); }
  101. HANDLE *GetHdl(void) { return &m_hdl; };
  102. void Init(LPTSTR szName, HRESULT &hr);
  103. private:
  104. HANDLE m_hdl;
  105. };
  106. void CEvent::Init(LPTSTR szName, HRESULT &hr)
  107. {
  108. hr = S_OK;
  109. // first try opening the event
  110. m_hdl = OpenEvent(EVENT_ALL_ACCESS,
  111. FALSE,
  112. szName);
  113. if (m_hdl == NULL)
  114. {
  115. // doesnt exist yet so create it.
  116. m_hdl = CreateEvent(NULL, // security
  117. FALSE, // auto reset
  118. FALSE, // initially not signalled
  119. szName);
  120. if (m_hdl == NULL)
  121. {
  122. _tprintf (TEXT("Error Creating CEvent (%s)\n"), szName);
  123. hr = GetLastError();
  124. }
  125. else
  126. {
  127. _tprintf (TEXT("Created CEvent (%s)\n"), szName);
  128. }
  129. }
  130. else
  131. {
  132. _tprintf (TEXT("Opened CEvent (%s)\n"), szName);
  133. }
  134. }
  135. //--------------------------------------------------------------------------
  136. //
  137. // Class: CTaskSwitch
  138. //
  139. // Purpose: class for timing task switches.
  140. //
  141. //--------------------------------------------------------------------------
  142. class CTaskSwitch
  143. {
  144. public:
  145. CTaskSwitch(LPSTR lpszCmdLine, HRESULT &hr);
  146. ~CTaskSwitch(void);
  147. int MainProcessLoop(void);
  148. LRESULT ThreadWndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam);
  149. HRESULT SpawnOtherSide(void);
  150. private:
  151. // initialization / cleanup methods
  152. HRESULT ParseCmdLine(LPSTR lpszCmdLine, SExecParms &execp);
  153. HRESULT WindowInitialize(WNDPROC pfnWndProc, HWND &hWnd);
  154. void WindowUninitialize(HWND hWnd);
  155. void CreateOtherParms(void);
  156. void WriteExecParms(void);
  157. void WriteResults(void);
  158. void Help(void);
  159. DWORD GetWaitType(LPSTR pszCmd);
  160. DWORD GetSignalType(LPSTR pszCmd);
  161. DWORD CreateProc(void);
  162. // processing methods
  163. HRESULT SendOrWaitFirstSignal(void);
  164. void ProcessMsgWaitForMultiple(DWORD dwRet);
  165. void ProcessIncommingEvent(void);
  166. void UpdateLoopCounters(void);
  167. void SignalOtherSide(void);
  168. // data
  169. BOOL g_fDone; // when to exit the loop
  170. BOOL g_fKicker; // we kick the other guy
  171. BOOL g_fThreadSwitch; // thread or process switching?
  172. BOOL g_fWaitMultiple; // wait single or multiple
  173. ULONG g_oloop; // outer loop counter
  174. ULONG g_iloop; // inner loop counter
  175. DWORD g_WaitType; // what to wait on
  176. DWORD g_SignalType; // what to signal
  177. // used only for parameter parseing
  178. DWORD g_WaitType1; // what to wait on
  179. DWORD g_SignalType1; // what to signal
  180. DWORD g_WaitType2; // what to wait on
  181. DWORD g_SignalType2; // what to signal
  182. HWND g_hWndOther; // hWnd of other side
  183. HWND g_hWndMe; // my hWnd
  184. CEvent g_WaitEvent; // event to wait on
  185. CEvent g_SignalEvent; // event to signal
  186. ULONG g_time[MAX_OLOOP]; // place to store the timings.
  187. CStopWatch g_timer; // global timer proc 1
  188. SExecParms g_execp; // execution parameters
  189. CTestOutput * g_output; // output log
  190. HRESULT g_hr; // result code
  191. CHAR g_szOtherParms[MAX_PATH]; // parm string for other guy
  192. };
  193. // task switch objects - must be global for ThreadWndProc
  194. CTaskSwitch *g_pTaskSwitch1 = NULL;
  195. CTaskSwitch *g_pTaskSwitch2 = NULL;
  196. //--------------------------------------------------------------------------
  197. //
  198. // WinMain - main entry point of program. May just call ThreadEntry, or
  199. // may spawn another thread in the case of thread switching.
  200. //
  201. //
  202. //--------------------------------------------------------------------------
  203. int WinMain(HINSTANCE hinst, HINSTANCE hPrev, LPSTR lpszCmdLine, int CmdShow)
  204. {
  205. HRESULT hr;
  206. // create the first task switch object for this process
  207. g_pTaskSwitch1 = new CTaskSwitch(lpszCmdLine, hr);
  208. if (hr == S_OK)
  209. {
  210. // spawn a new thread or a new process to do task switching with.
  211. hr = g_pTaskSwitch1->SpawnOtherSide();
  212. if (hr == S_OK)
  213. {
  214. // enter the main processing loop
  215. g_pTaskSwitch1->MainProcessLoop();
  216. }
  217. }
  218. // print the results
  219. delete g_pTaskSwitch1;
  220. return 1;
  221. }
  222. //--------------------------------------------------------------------------
  223. //
  224. // ThreadEntry - main entry point for a thread spawned by CreateThread
  225. // in the case of task switching between threads.
  226. //
  227. // Creates an instance of the CTaskSwitch class and invokes it
  228. // main function.
  229. //
  230. //--------------------------------------------------------------------------
  231. DWORD ThreadEntry(void *param)
  232. {
  233. LPSTR lpszCmdLine = (LPSTR) param;
  234. HRESULT hr;
  235. // create the second task switch object for this process
  236. g_pTaskSwitch2 = new CTaskSwitch(lpszCmdLine, hr);
  237. if (hr == S_OK)
  238. {
  239. // enter the main processing loop
  240. g_pTaskSwitch2->MainProcessLoop();
  241. }
  242. // print the results
  243. delete g_pTaskSwitch2;
  244. return hr;
  245. }
  246. //--------------------------------------------------------------------------
  247. //
  248. // Dipatch to the correct CTaskSwitch object if the message is our
  249. // special message.
  250. //
  251. //--------------------------------------------------------------------------
  252. LRESULT ThreadWndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
  253. {
  254. if (msg == g_MyMessage)
  255. {
  256. // its my special message, go handle it.
  257. // here i have to select which object to dispatch to for the
  258. // multithreaded case. i base that decision on lparam.
  259. if (lparam == 0)
  260. {
  261. // use the first task switch object
  262. return g_pTaskSwitch1->ThreadWndProc(hWnd, msg, wparam, lparam);
  263. }
  264. else
  265. {
  266. // use the second task switch object
  267. return g_pTaskSwitch2->ThreadWndProc(hWnd, msg, wparam, lparam);
  268. }
  269. }
  270. else
  271. {
  272. // let the default window procedure have the message.
  273. return DefWindowProc(hWnd, msg, wparam, lparam);
  274. }
  275. }
  276. //--------------------------------------------------------------------------
  277. //
  278. // Constructor : parse the command line, create the events, create the
  279. // window, and open a log file.
  280. //
  281. //--------------------------------------------------------------------------
  282. CTaskSwitch::CTaskSwitch(LPSTR lpszCmdLine, HRESULT &hr) :
  283. g_fDone(FALSE),
  284. g_fKicker(FALSE),
  285. g_fThreadSwitch(FALSE),
  286. g_fWaitMultiple(FALSE),
  287. g_oloop(10),
  288. g_iloop(100),
  289. g_WaitType(WAIT_EVENT),
  290. g_SignalType(SIGNAL_EVENT),
  291. g_hWndMe(NULL),
  292. g_hWndOther(NULL),
  293. g_output(NULL),
  294. g_hr(S_OK)
  295. {
  296. // parse command line and write the parms to log file.
  297. g_hr = ParseCmdLine(lpszCmdLine, g_execp);
  298. if (g_hr == S_OK)
  299. {
  300. // Create a log file & write execution parameters
  301. g_output = new CTestOutput(g_execp.szFile);
  302. WriteExecParms();
  303. // create the window for this thread
  304. g_hr = WindowInitialize(g_execp.pfnWndProc, g_hWndMe);
  305. if (g_hr == S_OK)
  306. {
  307. // Create the Wait event.
  308. g_WaitEvent.Init(g_execp.szWaitEvent, g_hr);
  309. if (g_hr == S_OK)
  310. {
  311. // Create the Signal event.
  312. g_SignalEvent.Init(g_execp.szSignalEvent, g_hr);
  313. if (g_hr == S_OK)
  314. {
  315. // create paramters to send to other side.
  316. CreateOtherParms();
  317. }
  318. }
  319. }
  320. }
  321. // return the results
  322. hr = g_hr;
  323. }
  324. //--------------------------------------------------------------------------
  325. //
  326. // Desructor
  327. //
  328. //--------------------------------------------------------------------------
  329. CTaskSwitch::~CTaskSwitch(void)
  330. {
  331. // write the results
  332. WriteResults();
  333. // cleanup window registration
  334. WindowUninitialize(g_hWndMe);
  335. // close the log file
  336. delete g_output;
  337. }
  338. //--------------------------------------------------------------------------
  339. //
  340. // Spawns either another process or another thread to perform the task
  341. // switching with.
  342. //
  343. //--------------------------------------------------------------------------
  344. HRESULT CTaskSwitch::SpawnOtherSide(void)
  345. {
  346. if (g_fKicker)
  347. {
  348. // i'm already the second entry, dont spawn anything.
  349. // sleep for a bit to make sure both sides are ready
  350. // when i kick things off.
  351. Sleep(1000);
  352. return S_OK;
  353. }
  354. if (g_fThreadSwitch)
  355. {
  356. // spawn a thread
  357. HANDLE hdl;
  358. DWORD dwId;
  359. hdl = CreateThread(NULL, // default security
  360. 0, // default stack size
  361. ThreadEntry, // entry point
  362. g_szOtherParms, // command line parms
  363. 0, // flags
  364. &dwId); // threadid
  365. if (hdl)
  366. {
  367. // dont need the handle
  368. CloseHandle(hdl);
  369. }
  370. else
  371. {
  372. // what went wrong?
  373. return GetLastError();
  374. }
  375. }
  376. else
  377. {
  378. // spawn a process
  379. DWORD dwRet = CreateProc();
  380. if (dwRet != S_OK)
  381. {
  382. return dwRet;
  383. }
  384. }
  385. return S_OK;
  386. }
  387. //--------------------------------------------------------------------------
  388. //
  389. // MainProcessLoop - does the main wait & process the event
  390. //
  391. //--------------------------------------------------------------------------
  392. int CTaskSwitch::MainProcessLoop(void)
  393. {
  394. MSG msg;
  395. DWORD dwRet;
  396. // Send, or wait on, the first signal.
  397. SendOrWaitFirstSignal();
  398. // Reset the timer and enter the main loop.
  399. g_timer.Reset();
  400. // wait loop - based on the type of event we should receive, we
  401. // wait here until such an event occurs. Then we send a signal
  402. // to the other side based on what it expects from us.
  403. while (!g_fDone)
  404. {
  405. switch (g_WaitType)
  406. {
  407. case WAIT_MSGWAITFORMULTIPLE:
  408. // wait here for a message or an event to be signalled
  409. dwRet = MsgWaitForMultipleObjects(1,
  410. g_WaitEvent.GetHdl(),
  411. FALSE,
  412. 600000,
  413. QS_ALLINPUT);
  414. // Dispatch to ThreadWndProc if a message, or
  415. // to ProcessEvent if an event was signalled
  416. ProcessMsgWaitForMultiple(dwRet);
  417. break;
  418. case WAIT_GETMESSAGE:
  419. // wait for a windows message
  420. if (GetMessage(&msg, NULL, 0, 0))
  421. {
  422. // dispatches to my ThreadWndProc
  423. DispatchMessage(&msg);
  424. }
  425. break;
  426. case WAIT_EVENT:
  427. // wait for the event to be signalled
  428. if (g_fWaitMultiple)
  429. g_WaitEvent.BlockM();
  430. else
  431. g_WaitEvent.BlockS();
  432. // process the event
  433. ProcessIncommingEvent();
  434. break;
  435. case WAIT_SYNCHRONOUS:
  436. // we have a synchronous singal to the other side, so there is
  437. // nothing to wait on, we will just go make another synchronous
  438. // call. this is valid only if the g_SignalType is
  439. // SIGNAL_SENDMESSAGE.
  440. ProcessIncommingEvent();
  441. break;
  442. default:
  443. // unknown event
  444. break;
  445. }
  446. } // while
  447. return msg.wParam; // Return value from PostQuitMessage
  448. }
  449. //--------------------------------------------------------------------------
  450. //
  451. // processes a wakeup from MsgWaitForMultiple. Determines if the event was
  452. // a message arrival (in which case it Peeks it and Dispatches it, or if it
  453. // was an event signalled, in which case it calls the event handler.
  454. //
  455. //--------------------------------------------------------------------------
  456. void CTaskSwitch::ProcessMsgWaitForMultiple(DWORD dwRet)
  457. {
  458. MSG msg;
  459. if (dwRet == WAIT_OBJECT_0)
  460. {
  461. // our event got signalled, update the counters
  462. ProcessIncommingEvent();
  463. }
  464. else if (dwRet == WAIT_OBJECT_0 + 1)
  465. {
  466. // some windows message was received. dispatch it.
  467. if (PeekMessage(&msg, g_hWndMe, 0, 0, PM_REMOVE))
  468. {
  469. DispatchMessage(&msg);
  470. }
  471. }
  472. else
  473. {
  474. // our event timed out or our event was abandoned or
  475. // an error occurred.
  476. g_fDone = TRUE;
  477. }
  478. }
  479. //--------------------------------------------------------------------------
  480. //
  481. // processes an incomming event. Just updates the counters and
  482. // signals the other side.
  483. //
  484. //--------------------------------------------------------------------------
  485. void CTaskSwitch::ProcessIncommingEvent(void)
  486. {
  487. // update the loop counters
  488. UpdateLoopCounters();
  489. // Signal the other side
  490. SignalOtherSide();
  491. }
  492. //--------------------------------------------------------------------------
  493. //
  494. // process the incomming message
  495. //
  496. //--------------------------------------------------------------------------
  497. LRESULT CTaskSwitch::ThreadWndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
  498. {
  499. // save the callers hWnd
  500. g_hWndOther = (HWND) wparam;
  501. // process as usual
  502. ProcessIncommingEvent();
  503. return 0;
  504. }
  505. //--------------------------------------------------------------------------
  506. //
  507. // updates the global loop counters, reseting the time when the inner
  508. // loop counter expires, and setting the fDone when the outer and inner
  509. // loop counters expire.
  510. //
  511. //--------------------------------------------------------------------------
  512. void CTaskSwitch::UpdateLoopCounters(void)
  513. {
  514. if (g_iloop == 0)
  515. {
  516. // get time for latest outer loop
  517. g_time[g_oloop] = g_timer.Read();
  518. if (g_oloop == 0)
  519. {
  520. // that was the last outerloop, we're done.
  521. g_fDone = TRUE;
  522. }
  523. else
  524. {
  525. // update the counters
  526. g_iloop = g_execp.iloop;
  527. --g_oloop;
  528. // restart the timer
  529. g_timer.Reset();
  530. }
  531. }
  532. else
  533. {
  534. // just update the inner loop count
  535. --g_iloop;
  536. }
  537. }
  538. //--------------------------------------------------------------------------
  539. //
  540. // signals the other process or thread according to the SendType (either
  541. // signals an event or posts a message).
  542. //
  543. //--------------------------------------------------------------------------
  544. void CTaskSwitch::SignalOtherSide(void)
  545. {
  546. switch (g_SignalType)
  547. {
  548. case SIGNAL_EVENT:
  549. // signal the other sides event
  550. g_SignalEvent.Signal();
  551. break;
  552. case SIGNAL_POSTMESSAGE:
  553. // post a message to the other sides window handle.
  554. // lparam tells ThreadWndProc which object to dispatch to, either
  555. // g_pTaskSwitch1 or g_pTaskSwitch2. We only go to 2 if we are
  556. // doing thread switches AND the poster is not the kicker.
  557. PostMessage(g_hWndOther,
  558. g_MyMessage,
  559. (WPARAM)g_hWndMe,
  560. (g_fThreadSwitch && !g_fKicker));
  561. break;
  562. case SIGNAL_SENDMESSAGE:
  563. // send a message to the other side. this is a synchronous
  564. // event. see comment in PostMessage above regarding lparam.
  565. SendMessage(g_hWndOther,
  566. g_MyMessage,
  567. (WPARAM)g_hWndMe,
  568. (g_fThreadSwitch && !g_fKicker));
  569. break;
  570. case SIGNAL_SYNCHRONOUS:
  571. // the event we received is a synchronous event. there is no need
  572. // to do anything to wake the other side.
  573. break;
  574. default:
  575. // unknown signal type
  576. break;
  577. }
  578. }
  579. //--------------------------------------------------------------------------
  580. //
  581. // signals the other process or thread that it can begin the test. this
  582. // avoids timings skewed due to process startup latency.
  583. //
  584. //--------------------------------------------------------------------------
  585. HRESULT CTaskSwitch::SendOrWaitFirstSignal(void)
  586. {
  587. if (g_fKicker)
  588. {
  589. // send a signal to drop the otherside into its wait loop, and
  590. // then call SignalOtherSide to start the cycle, kicking the
  591. // other side out of his first wait.
  592. printf ("Initial Signal to Other Side\n");
  593. g_SignalEvent.Signal();
  594. SignalOtherSide();
  595. }
  596. else
  597. {
  598. printf ("Waiting for Signal From Other Side\n");
  599. g_WaitEvent.BlockS();
  600. }
  601. return S_OK;
  602. }
  603. //--------------------------------------------------------------------------
  604. //
  605. // initializes the window with the specified window proc.
  606. //
  607. //--------------------------------------------------------------------------
  608. HRESULT CTaskSwitch::WindowInitialize(WNDPROC pfnWndProc, HWND &hWnd)
  609. {
  610. if (!g_MyMessage)
  611. {
  612. // Register my message type
  613. g_MyMessage = RegisterWindowMessage(
  614. TEXT("Component Object Model Remote Request Arrival") );
  615. }
  616. if (!g_MyClass)
  617. {
  618. // Register my window class.
  619. WNDCLASS wcls;
  620. wcls.style = 0;
  621. wcls.lpfnWndProc = pfnWndProc;
  622. wcls.cbClsExtra = 0;
  623. wcls.cbWndExtra = 0;
  624. wcls.hInstance = g_hInst;
  625. wcls.hIcon = NULL;
  626. wcls.hCursor = NULL;
  627. wcls.hbrBackground = (HBRUSH) COLOR_BACKGROUND + 1;
  628. wcls.lpszMenuName = NULL;
  629. wcls.lpszClassName = MY_WINDOW_CLASS;
  630. g_MyClass = RegisterClass( &wcls );
  631. }
  632. if (g_MyClass)
  633. {
  634. // Create a hidden window.
  635. hWnd = CreateWindowEx( 0,
  636. (LPCTSTR) g_MyClass,
  637. TEXT("Task Switcher"),
  638. WS_DISABLED,
  639. CW_USEDEFAULT,
  640. CW_USEDEFAULT,
  641. CW_USEDEFAULT,
  642. CW_USEDEFAULT,
  643. NULL,
  644. NULL,
  645. g_hInst,
  646. NULL );
  647. if (hWnd)
  648. {
  649. printf ("Created Window with hWnd %x\n", hWnd);
  650. return S_OK;
  651. }
  652. }
  653. return E_OUTOFMEMORY;
  654. }
  655. //--------------------------------------------------------------------------
  656. //
  657. // destroys the window and unregisters the class.
  658. //
  659. //--------------------------------------------------------------------------
  660. void CTaskSwitch::WindowUninitialize(HWND hWnd)
  661. {
  662. if (hWnd != NULL)
  663. {
  664. DestroyWindow(hWnd);
  665. }
  666. if (g_MyClass != 0)
  667. {
  668. UnregisterClass(MY_WINDOW_CLASS, g_hInst);
  669. }
  670. }
  671. //--------------------------------------------------------------------------
  672. //
  673. // parses the command line and returns the execution parameters
  674. //
  675. //--------------------------------------------------------------------------
  676. HRESULT CTaskSwitch::ParseCmdLine(LPSTR lpszCmdLine, SExecParms &execp)
  677. {
  678. BOOL fFile = FALSE;
  679. // set the default values for execution parameters.
  680. execp.oloop = 10;
  681. execp.iloop = 100;
  682. execp.hWndOther = NULL;
  683. execp.pfnWndProc = ::ThreadWndProc;
  684. // check the input parameters
  685. LPSTR pszCmd = lpszCmdLine;
  686. LPSTR pszCmdNext = NULL;
  687. while (pszCmd)
  688. {
  689. pszCmdNext = strchr(pszCmd, ' ');
  690. if (pszCmdNext)
  691. {
  692. *pszCmdNext = '\0';
  693. pszCmdNext++;
  694. }
  695. // check for outer loop count
  696. if (!_strnicmp(pszCmd, "/o:", 3))
  697. {
  698. execp.oloop = atoi (pszCmd+3);
  699. if (execp.oloop > MAX_OLOOP)
  700. execp.oloop = MAX_OLOOP;
  701. }
  702. // check for inner loop count
  703. else if (!_strnicmp(pszCmd, "/i:", 3))
  704. {
  705. execp.iloop = atoi (pszCmd+3);
  706. if (execp.iloop < 1)
  707. execp.iloop = 1;
  708. }
  709. // check for window handle
  710. else if (!_strnicmp(pszCmd, "/hwnd:", 6))
  711. {
  712. execp.hWndOther = (HWND) atoi (pszCmd+6);
  713. }
  714. // check for waiter or Kicker
  715. else if (!_strnicmp(pszCmd, "/k", 2))
  716. {
  717. g_fKicker = TRUE;
  718. }
  719. // check for thread or process switch
  720. else if (!_strnicmp(pszCmd, "/p", 2))
  721. {
  722. g_fThreadSwitch = FALSE;
  723. }
  724. // check for thread or process switch
  725. else if (!_strnicmp(pszCmd, "/t", 2))
  726. {
  727. g_fThreadSwitch = TRUE;
  728. }
  729. // check for wait single or multiple
  730. else if (!_strnicmp(pszCmd, "/m", 2))
  731. {
  732. g_fWaitMultiple = TRUE;
  733. }
  734. // check for wait event name
  735. else if (!_strnicmp(pszCmd, "/e1:", 4))
  736. {
  737. #ifdef UNICODE
  738. mbstowcs(execp.szWaitEvent, pszCmd+4, strlen(pszCmd+4)+1);
  739. #else
  740. strcpy(execp.szWaitEvent, pszCmd+4);
  741. #endif
  742. }
  743. // check for signal event name
  744. else if (!_strnicmp(pszCmd, "/e2:", 4))
  745. {
  746. #ifdef UNICODE
  747. mbstowcs(execp.szSignalEvent, pszCmd+4, strlen(pszCmd+4)+1);
  748. #else
  749. strcpy(execp.szSignalEvent, pszCmd+4);
  750. #endif
  751. }
  752. // check for output file name
  753. else if (!_strnicmp(pszCmd, "/f:", 3))
  754. {
  755. fFile = TRUE;
  756. #ifdef UNICODE
  757. mbstowcs(execp.szFile, pszCmd+3, strlen(pszCmd+3)+1);
  758. #else
  759. strcpy(execp.szFile, pszCmd+3);
  760. #endif
  761. }
  762. // check for wait type
  763. else if (!_strnicmp(pszCmd, "/w1:", 4))
  764. {
  765. g_WaitType1 = GetWaitType(pszCmd+4);
  766. }
  767. else if (!_strnicmp(pszCmd, "/w2:", 4))
  768. {
  769. g_WaitType2 = GetWaitType(pszCmd+4);
  770. }
  771. // check for signal type
  772. else if (!_strnicmp(pszCmd, "/s1:", 4))
  773. {
  774. g_SignalType1 = GetSignalType(pszCmd+4);
  775. }
  776. else if (!_strnicmp(pszCmd, "/s2:", 4))
  777. {
  778. g_SignalType2 = GetSignalType(pszCmd+4);
  779. }
  780. // check for help request
  781. else if ((!_strnicmp(pszCmd, "/?", 2)) || (!_strnicmp(pszCmd, "/h", 2)))
  782. {
  783. Help();
  784. return -1;
  785. }
  786. pszCmd = pszCmdNext;
  787. }
  788. g_iloop = execp.iloop;
  789. g_oloop = execp.iloop;
  790. g_hWndOther = execp.hWndOther;
  791. if (g_fKicker)
  792. {
  793. g_WaitType = g_WaitType2;
  794. g_SignalType = g_SignalType2;
  795. if (!fFile)
  796. _tcscpy(execp.szFile, TEXT("kicker"));
  797. }
  798. else
  799. {
  800. g_WaitType = g_WaitType1;
  801. g_SignalType = g_SignalType1;
  802. if (!fFile)
  803. _tcscpy(execp.szFile, TEXT("waiter"));
  804. }
  805. return S_OK;
  806. }
  807. DWORD CTaskSwitch::GetWaitType(LPSTR pszCmd)
  808. {
  809. ULONG i=0;
  810. while (aszWait[++i]) // slot 0 is not used
  811. {
  812. if (!_stricmp(pszCmd, aszWait[i]))
  813. return i;
  814. }
  815. Help();
  816. return 0;
  817. }
  818. DWORD CTaskSwitch::GetSignalType(LPSTR pszCmd)
  819. {
  820. ULONG i=0;
  821. while (aszSignal[++i]) // slot 0 is not used
  822. {
  823. if (!_stricmp(pszCmd, aszSignal[i]))
  824. return i;
  825. }
  826. Help();
  827. return 0;
  828. }
  829. //--------------------------------------------------------------------------
  830. //
  831. // creates the command line parameters for the other guy
  832. //
  833. //--------------------------------------------------------------------------
  834. void CTaskSwitch::CreateOtherParms(void)
  835. {
  836. // write the formatted parms to the parm string
  837. sprintf(g_szOtherParms, "/k %s %s /i:%d /o:%d /hWnd:%ld "
  838. "/e1:%hs /e2:%hs /w1:%s /s1:%s /w2:%s /s2:%s",
  839. (g_fThreadSwitch) ? "/t" : "/p",
  840. (g_fWaitMultiple) ? "/m" : " ",
  841. g_execp.iloop, // same loop counts as me
  842. g_execp.oloop,
  843. g_hWndMe, // posts to my window
  844. g_execp.szSignalEvent, // it waits on my signal event
  845. g_execp.szWaitEvent, // it signals my wait event
  846. aszWait[g_WaitType1], // signal what i wait on
  847. aszSignal[g_SignalType1], // wait on what i signal
  848. aszWait[g_WaitType2], // signal what i wait on
  849. aszSignal[g_SignalType2]); // wait on what i signal
  850. }
  851. //--------------------------------------------------------------------------
  852. //
  853. // writes the execution parameters to a log file.
  854. //
  855. //--------------------------------------------------------------------------
  856. void CTaskSwitch::WriteExecParms()
  857. {
  858. // write the run parameters to the output file
  859. g_output->WriteString(TEXT("Using Parametes:\n"));
  860. g_output->WriteResult(TEXT("\tInner Loop Count = "), g_execp.iloop);
  861. g_output->WriteResult(TEXT("\tOuter Loop Count = "), g_execp.oloop);
  862. g_output->WriteString(TEXT("\n\n"));
  863. // flush to avoid disk io during the test
  864. g_output->Flush();
  865. }
  866. //--------------------------------------------------------------------------
  867. //
  868. // writes the results to a log file.
  869. //
  870. //--------------------------------------------------------------------------
  871. void CTaskSwitch::WriteResults(void)
  872. {
  873. if (g_hr == S_OK)
  874. {
  875. // compute the averages
  876. ULONG tTotal = 0;
  877. // skip the first & last value as they are sometimes skewed
  878. for (int i=0; i<g_execp.oloop; i++)
  879. {
  880. tTotal += g_time[i];
  881. }
  882. // compute average for 1 call/response
  883. tTotal /= (g_execp.oloop * g_execp.iloop);
  884. // display the results
  885. g_output->WriteResults(TEXT("Times "), g_execp.oloop, g_time);
  886. g_output->WriteResult(TEXT("\nAverage "), tTotal);
  887. }
  888. }
  889. //--------------------------------------------------------------------------
  890. //
  891. // writes the help info to the screen
  892. //
  893. //--------------------------------------------------------------------------
  894. void CTaskSwitch::Help()
  895. {
  896. printf ("msgtask\n");
  897. printf ("\t/o:<nnn> - outer loop count def 10\n");
  898. printf ("\t/i:<nnn> - inner loop count def 100\n");
  899. printf ("\t/f:<name> - name of output file. def [kick | wait]\n");
  900. printf ("\t/w1:<event|getmsg|msgwait> - what to wait on\n");
  901. printf ("\t/s1:<event|postmsg> - what to signal\n");
  902. printf ("\t/w2:<event|getmsg|msgwait> - what to wait on\n");
  903. printf ("\t/s2:<event|postmsg> - what to signal\n");
  904. printf ("\t/e1:<name> - name of wait event\n");
  905. printf ("\t/e2:<name> - name of signal event\n");
  906. printf ("\t/k - kicker (as opposed to waiter)\n");
  907. printf ("\t/t - use thread switching\n");
  908. printf ("\t/p - use process switching\n");
  909. printf ("\t/m - use WaitMultiple vs WaitSingle\n");
  910. printf ("\t/hWnd:<nnnn> - window handle of other side\n");
  911. printf ("\n");
  912. printf ("timings are given for the inner loop count calls\n");
  913. return;
  914. }
  915. //--------------------------------------------------------------------------
  916. //
  917. // creates a process
  918. //
  919. //--------------------------------------------------------------------------
  920. DWORD CTaskSwitch::CreateProc(void)
  921. {
  922. // create the command line
  923. TCHAR szCmdLine[256];
  924. _stprintf(szCmdLine, TEXT("ntsd procswap %hs"), g_szOtherParms);
  925. // build the win32 startup info structure
  926. STARTUPINFO startupinfo;
  927. startupinfo.cb = sizeof(STARTUPINFO);
  928. startupinfo.lpReserved = NULL;
  929. startupinfo.lpDesktop = NULL;
  930. startupinfo.lpTitle = TEXT("Task Switcher");
  931. startupinfo.dwX = 40;
  932. startupinfo.dwY = 40;
  933. startupinfo.dwXSize = 80;
  934. startupinfo.dwYSize = 40;
  935. startupinfo.dwFlags = 0;
  936. startupinfo.wShowWindow = SW_SHOWNORMAL;
  937. startupinfo.cbReserved2 = 0;
  938. startupinfo.lpReserved2 = NULL;
  939. PROCESS_INFORMATION ProcInfo;
  940. BOOL fRslt = CreateProcess(NULL, // app name
  941. szCmdLine, // command line
  942. NULL, // lpsaProcess
  943. NULL, // lpsaThread
  944. FALSE, // inherit handles
  945. CREATE_NEW_CONSOLE,// creation flags
  946. NULL, // lpEnvironment
  947. NULL, // curr Dir
  948. &startupinfo, // Startup Info
  949. &ProcInfo); // process info
  950. if (fRslt)
  951. {
  952. // we dont need the handles
  953. CloseHandle(ProcInfo.hProcess);
  954. CloseHandle(ProcInfo.hThread);
  955. printf ("Created Process (%ws) pid=%x\n", szCmdLine, ProcInfo.dwProcessId);
  956. return S_OK;
  957. }
  958. else
  959. {
  960. // what went wrong?
  961. DWORD dwRet = GetLastError();
  962. printf ("CreateProcess (%ws) failed %x\n", szCmdLine, dwRet);
  963. return dwRet;
  964. }
  965. }
  966.