4 * PROJECT: System regression tool for ReactOS
5 * LICENSE: GPL - See COPYING in the top level directory
6 * FILE: tools/sysreg/conf_parser.h
7 * PURPOSE: ReactOS boot test
8 * PROGRAMMERS: Johannes Anderwald (johannes.anderwald at sbox tugraz at)
12 #include "rosboot_test.h"
13 #include "pipe_reader.h"
14 #include "namedpipe_reader.h"
15 //#include "sym_file.h"
16 #include "file_reader.h"
17 #include "os_support.h"
38 using System_::PipeReader
;
39 using System_::NamedPipeReader
;
40 /* using System_::SymbolFile; */
41 using System_::FileReader
;
42 using System_::OsSupport
;
43 using System_::EnvironmentVariable
;
47 typedef wofstream ofstream
;
52 string
RosBootTest::ROS_EMU_TYPE
= "ROS_EMU_TYPE";
53 string
RosBootTest::EMU_TYPE_QEMU
= "qemu";
54 string
RosBootTest::EMU_TYPE_VMWARE
= "vmware";
55 string
RosBootTest::ROS_HDD_IMAGE
= "ROS_HDD_IMAGE";
56 string
RosBootTest::ROS_CD_IMAGE
= "ROS_CD_IMAGE";
57 string
RosBootTest::ROS_MAX_TIME
= "ROS_MAX_TIME";
58 string
RosBootTest::ROS_LOG_FILE
= "ROS_LOG_FILE";
59 string
RosBootTest::ROS_SYM_DIR
= "ROS_SYM_DIR";
60 string
RosBootTest::ROS_DELAY_READ
= "ROS_DELAY_READ";
61 string
RosBootTest::ROS_SYSREG_CHECKPOINT
= "SYSREG_CHECKPOINT:";
62 string
RosBootTest::ROS_CRITICAL_IMAGE
= "ROS_CRITICAL_IMAGE";
63 string
RosBootTest::ROS_EMU_KILL
= "ROS_EMU_KILL";
64 string
RosBootTest::ROS_EMU_MEM
= "ROS_EMU_MEM";
65 string
RosBootTest::ROS_BOOT_CMD
= "ROS_BOOT_CMD";
68 string
RosBootTest::ROS_EMU_PATH
= "ROS_EMU_PATH_LIN";
70 string
RosBootTest::ROS_EMU_PATH
= "ROS_EMU_PATH_WIN";
73 //---------------------------------------------------------------------------------------
74 RosBootTest::RosBootTest() : m_MaxTime(65), m_DelayRead(0)
79 //---------------------------------------------------------------------------------------
80 RosBootTest::~RosBootTest()
84 //---------------------------------------------------------------------------------------
85 bool RosBootTest::executeBootCmd()
88 const char * args
[128];
92 pBuf
= (char*)m_BootCmd
.c_str ();
95 pBuf
= strtok(pBuf
, " ");
99 strcpy(szBuffer
, pBuf
);
101 args
[numargs
] = pBuf
;
103 pBuf
= strtok(NULL
, " ");
109 strcpy(szBuffer
, pBuf
);
111 m_Pid
= OsSupport::createProcess (szBuffer
, numargs
-1, args
, false);
114 cerr
<< "Error: failed to launch boot cmd " << m_BootCmd
<< endl
;
120 //---------------------------------------------------------------------------------------
121 void RosBootTest::delayRead()
126 /// delay reading until emulator is ready
129 OsSupport::delayExecution(m_DelayRead
);
132 //---------------------------------------------------------------------------------------
133 void RosBootTest::getDefaultHDDImage(string
& img
)
137 EnvironmentVariable::getValue("ROS_OUTPUT", img
);
144 //---------------------------------------------------------------------------------------
145 bool RosBootTest::isFileExisting(string output
)
148 file
= fopen(output
.c_str(), "r");
152 /* the file exists */
159 //---------------------------------------------------------------------------------------
160 bool RosBootTest::isDefaultHDDImageExisting()
164 getDefaultHDDImage(img
);
165 return isFileExisting(img
);
168 //---------------------------------------------------------------------------------------
169 bool RosBootTest::createHDDImage(string image
)
173 if (!getQemuDir(qemuimgdir
))
175 cerr
<< "Error: failed to retrieve qemu directory path" << endl
;
181 qemuimgdir
+= "/qemu-img";
184 qemuimgdir
+= "\\qemu-img.exe";
187 if (!isFileExisting(qemuimgdir
))
189 cerr
<< "Error: ROS_EMU_PATH must contain the path to qemu and qemu-img " << qemuimgdir
<< endl
;
192 remove(image
.c_str ());
194 const char * options
[] = {NULL
,
208 options
[0] = qemuimgdir
.c_str();
209 options
[4] = image
.c_str();
211 cerr
<< "Creating HDD Image ..." << image
<< endl
;
212 OsSupport::createProcess (qemuimgdir
.c_str(), 6, options
, true);
213 if (isFileExisting(image
))
220 //----------------------------------------------------------------------------------------
221 bool RosBootTest::isQemuPathValid()
225 if (m_BootCmd
.length())
227 /* the boot cmd is already provided
228 * check if path to qemu is valid
230 string::size_type pos
= m_BootCmd
.find_first_of(" ");
231 if (pos
== string::npos
)
233 /* the bootcmd is certainly not valid */
236 qemupath
= m_BootCmd
.substr(0, pos
);
240 /* the qemu path is provided ROS_EMU_PATH variable */
241 qemupath
= m_EmuPath
;
245 return isFileExisting(qemupath
);
247 //----------------------------------------------------------------------------------------
248 bool RosBootTest::hasQemuNoRebootOption()
257 //----------------------------------------------------------------------------------------
258 bool RosBootTest::getQemuDir(string
& qemupath
)
260 string::size_type pos
;
263 pos
= m_EmuPath
.find_last_of("/");
265 pos
= m_EmuPath
.find_last_of("\\");
267 if (pos
== string::npos
)
269 cerr
<< "Error: ROS_EMU_PATH is invalid!!!" << endl
;
272 qemupath
= m_EmuPath
.substr(0, pos
);
275 //----------------------------------------------------------------------------------------
276 bool RosBootTest::createBootCmd()
280 char pipename
[] = "qemuXXXXXX";
281 if (m_MaxMem
.length() == 0)
283 /* set default memory size to 64M */
289 if (mktemp(pipename
))
291 string temp
= pipename
;
292 m_Src
= "/tmp/" + temp
;
293 pipe
= "pipe:" + m_Src
;
298 pipe
= "pipe:/tmp/qemu";
302 qemudir
= "/usr/share/qemu";
303 m_DebugPort
= "pipe";
305 if (_mktemp(pipename
))
307 string temp
= pipename
;
308 pipe
= "pipe:" + temp
;
309 m_Src
= "\\\\.\\pipe\\" + temp
;
314 m_Src
= "\\\\.\\pipe\\qemu";
316 m_DebugPort
= "pipe";
318 if (!getQemuDir(qemudir
))
325 m_BootCmd
= m_EmuPath
+ " -L " + qemudir
+ " -m " + m_MaxMem
+ " -serial " + pipe
;
327 if (m_CDImage
.length())
329 /* boot from cdrom */
330 m_BootCmd
+= " -boot d -cdrom " + m_CDImage
;
332 if (m_HDDImage
.length ())
334 /* add disk when specified */
335 m_BootCmd
+= " -hda " + m_HDDImage
;
339 else if (m_HDDImage
.length ())
342 m_BootCmd
+= " -boot c -hda " + m_HDDImage
;
347 * no boot device provided
349 cerr
<< "Error: no bootdevice provided" << endl
;
354 /* on linux we need get pid in order to be able
355 * to terminate the emulator in case of errors
356 * on windows we can get pid as return of CreateProcess
358 m_PidFile
= "output-i386";
359 EnvironmentVariable::getValue("ROS_OUTPUT", m_PidFile
);
360 m_PidFile
+= "/pid.txt";
361 m_BootCmd
+= " -pidfile ";
362 m_BootCmd
+= m_PidFile
;
363 m_BootCmd
+= " -nographic";
366 if (hasQemuNoRebootOption())
368 m_BootCmd
+= " -no-reboot ";
373 //----------------------------------------------------------------------------------------
374 bool RosBootTest::extractPipeFromBootCmd()
376 string::size_type pos
= m_BootCmd
.find("-serial pipe:");
377 if (pos
== string::npos
)
379 /* no debug options provided */
380 cerr
<< "Error: provided boot cmd does not specify a pipe debugging port" << endl
;
384 string pipe
= m_BootCmd
.substr(pos
+ 13, m_BootCmd
.size() - pos
- 13);
385 pos
= pipe
.find(" ");
386 if (pos
!= string::npos
)
388 pipe
= pipe
.substr(0, pos
);
393 m_Src
= "\\\\.\\pipe\\" + pipe
.substr(0, pos
);
395 m_DebugPort
= "pipe";
398 //----------------------------------------------------------------------------------------
399 bool RosBootTest::configureHDDImage()
401 //cout << "configureHDDImage m_HDDImage " << m_HDDImage.length() << " m_CDImage " << m_CDImage.length() << " m_BootCmd: " << m_BootCmd.length() << endl;
402 if (m_HDDImage
.length())
404 /* check if ROS_HDD_IMAGE points to hdd image */
405 if (!isFileExisting(m_HDDImage
))
407 if (!m_CDImage
.length ())
409 cerr
<< "Error: HDD image is not existing and CDROM image not provided" << endl
;
413 return createHDDImage(m_HDDImage
);
417 else if (!m_BootCmd
.length ())
419 /* no hdd image provided
420 * but also no override by
423 if (!m_CDImage
.length ())
425 cerr
<< "Error: no HDD and CDROM image provided" << endl
;
429 getDefaultHDDImage(m_HDDImage
);
430 if (isFileExisting(m_HDDImage
))
432 cerr
<< "Falling back to default hdd image " << m_HDDImage
<< endl
;
435 return createHDDImage(m_HDDImage
);
438 * verify the provided ROS_BOOT_CMD for hdd image
442 bool hdaboot
= false;
443 string::size_type pos
= m_BootCmd
.find ("-boot c");
444 if (pos
!= string::npos
)
449 pos
= m_BootCmd
.find("-hda ");
450 if (pos
!= string::npos
)
452 string hdd
= m_BootCmd
.substr(pos
+ 5, m_BootCmd
.length() - pos
- 5);
455 cerr
<< "Error: ROS_BOOT_CMD misses value of -hda option" << endl
;
458 pos
= m_BootCmd
.find(" ");
459 if (pos
!= string::npos
)
462 /// sysreg assumes that the hdd image filename has no spaces
464 hdd
= hdd
.substr(0, pos
);
466 if (!isFileExisting(hdd
))
470 cerr
<< "Error: ROS_BOOT_CMD specifies booting from hda but no valid hdd image " << hdd
<< " provided" << endl
;
474 /* the file does not exist create it */
475 return createHDDImage(hdd
);
482 //----------------------------------------------------------------------------------------
483 bool RosBootTest::configureCDImage()
485 if (!m_BootCmd
.length ())
487 if (m_CDImage
.length())
489 /* we have a cd image lets check if its valid */
490 if (isFileExisting(m_CDImage
))
492 cerr
<< "Using CDROM image " << m_CDImage
<< endl
;
496 if (isFileExisting("ReactOS-RegTest.iso"))
498 m_CDImage
= "ReactOS-RegTest.iso";
499 cerr
<< "Falling back to default CDROM image " << m_CDImage
<< endl
;
502 cerr
<< "No CDROM image found, boot device is HDD" << endl
;
507 string::size_type pos
= m_BootCmd
.find("-boot ");
508 if (pos
== string::npos
)
510 /* ROS_BOOT_CMD must provide a boot parameter*/
511 cerr
<< "Error: ROS_BOOT_CMD misses boot parameter" << endl
;
515 string rest
= m_BootCmd
.substr(pos
+ 6, m_BootCmd
.length() - pos
- 6);
516 if (rest
.length() < 1)
518 /* boot parameter misses option where to boot from */
519 cerr
<< "Error: ROS_BOOT_CMD misses boot parameter" << endl
;
522 if (rest
[0] != 'c' && rest
[0] != 'd')
524 cerr
<< "Error: ROS_BOOT_CMD has invalid boot parameter" << endl
;
530 /* ROS_BOOT_CMD boots from hdd */
534 pos
= m_BootCmd
.find("-cdrom ");
535 if (pos
== string::npos
)
537 cerr
<< "Error: ROS_BOOT_CMD misses cdrom parameter" << endl
;
540 rest
= m_BootCmd
.substr(pos
+ 7, m_BootCmd
.length() - pos
- 7);
543 cerr
<< "Error: ROS_BOOT_CMD misses cdrom parameter" << endl
;
546 pos
= rest
.find(" ");
547 if (pos
!= string::npos
)
549 rest
= rest
.substr(0, pos
);
552 if (!isFileExisting(rest
))
554 cerr
<< "Error: cdrom image " << rest
<< " does not exist" << endl
;
563 //----------------------------------------------------------------------------------------
564 bool RosBootTest::configureQemu()
566 if (!isQemuPathValid())
568 cerr
<< "Error: the path to qemu is not valid" << endl
;
572 if (!configureCDImage())
574 cerr
<< "Error: failed to set a valid cdrom configuration" << endl
;
578 if (!configureHDDImage())
580 cerr
<< "Error: failed to set a valid hdd configuration" << endl
;
584 if (m_BootCmd
.length())
586 if (!extractPipeFromBootCmd())
593 if (!createBootCmd())
599 if (mkfifo(m_Src
.c_str(), 400))
604 cerr <<"Error: mkfifo failed with " << errno << endl;
611 if (m_PidFile
.length () && isFileExisting(m_PidFile
))
613 cerr
<< "Deleting pid file " << m_PidFile
<< endl
;
614 remove(m_PidFile
.c_str ());
619 cerr
<< "Opening Data Source:" << m_BootCmd
<< endl
;
620 m_DataSource
= new NamedPipeReader();
621 if (!executeBootCmd())
623 cerr
<< "Error: failed to launch emulator with: " << m_BootCmd
<< endl
;
630 //---------------------------------------------------------------------------------------
631 bool RosBootTest::configureVmWare()
633 cerr
<< "VmWare is currently not yet supported" << endl
;
636 //---------------------------------------------------------------------------------------
637 bool RosBootTest::readConfigurationValues(ConfigParser
&conf_parser
)
640 if (!conf_parser
.getStringValue(RosBootTest::ROS_EMU_TYPE
, m_EmuType
))
642 cerr
<< "Error: ROS_EMU_TYPE is not set" << endl
;
647 if (!conf_parser
.getStringValue(RosBootTest::ROS_EMU_PATH
, m_EmuPath
))
649 cerr
<< "Error: ROS_EMU_PATH is not set" << endl
;
652 if (!m_HDDImage
.length())
654 /* only read image once */
655 conf_parser
.getStringValue (RosBootTest::ROS_HDD_IMAGE
, m_HDDImage
);
657 if (!m_CDImage
.length())
659 /* only read cdimage once */
660 conf_parser
.getStringValue (RosBootTest::ROS_CD_IMAGE
, m_CDImage
);
666 conf_parser
.getIntValue (RosBootTest::ROS_MAX_TIME
, m_MaxTime
);
667 conf_parser
.getStringValue (RosBootTest::ROS_LOG_FILE
, m_DebugFile
);
668 conf_parser
.getStringValue (RosBootTest::ROS_SYM_DIR
, m_SymDir
);
669 conf_parser
.getIntValue (RosBootTest::ROS_DELAY_READ
, m_DelayRead
);
670 conf_parser
.getStringValue (RosBootTest::ROS_SYSREG_CHECKPOINT
, m_Checkpoint
);
671 conf_parser
.getStringValue (RosBootTest::ROS_CRITICAL_IMAGE
, m_CriticalImage
);
672 conf_parser
.getStringValue (RosBootTest::ROS_EMU_KILL
, m_KillEmulator
);
673 conf_parser
.getStringValue (RosBootTest::ROS_EMU_MEM
, m_MaxMem
);
674 conf_parser
.getStringValue (RosBootTest::ROS_BOOT_CMD
, m_BootCmd
);
678 //---------------------------------------------------------------------------------------
679 void RosBootTest::cleanup()
681 m_DataSource
->closeSource();
682 OsSupport::delayExecution(3);
686 OsSupport::terminateProcess (m_Pid
, 0);
691 if (m_PidFile
.length ())
693 remove(m_PidFile
.c_str ());
697 //---------------------------------------------------------------------------------------
698 bool RosBootTest::execute(ConfigParser
&conf_parser
)
700 if (!readConfigurationValues(conf_parser
))
705 if (m_EmuType
== EMU_TYPE_QEMU
)
707 if (!configureQemu())
709 cerr
<< "Error: failed to configure qemu" << endl
;
713 else if (m_EmuType
== EMU_TYPE_VMWARE
)
715 if (!configureVmWare())
717 cerr
<< "Error: failed to configure vmware" << endl
;
724 /// unsupported emulator
726 cerr
<< "Error: ROS_EMU_TYPE value is not supported:" << m_EmuType
<< "=" << EMU_TYPE_QEMU
<< endl
;
730 if (!configureQemu())
732 cerr
<< "Error: failed to configure qemu" << endl
;
739 cerr
<< "Delaying read for " << m_DelayRead
<< " seconds" << endl
;
740 OsSupport::delayExecution(m_DelayRead
);
743 if (!m_DataSource
->openSource(m_Src
))
745 cerr
<< "Error: failed to open data source with " << m_Src
<< endl
;
751 * For linux systems we can only
752 * check if the emulator runs by
753 * opening pid.txt and lookthrough if
757 FILE * file
= fopen(m_PidFile
.c_str(), "r");
760 cerr
<< "Error: failed to launch emulator" << endl
;
765 if (!fread(buffer
, 1, sizeof(buffer
), file
))
767 cerr
<< "Error: pid file w/o pid!!! " << endl
;
772 m_Pid
= atoi(buffer
);
775 OsSupport::cancelAlarms();
777 //OsSupport::setAlarm (m_MaxTime, m_Pid);
778 //OsSupport::setAlarm(m_MaxTime, getpid());
780 OsSupport::setAlarm (m_MaxTime
, m_Pid
);
781 OsSupport::setAlarm(m_MaxTime
, GetCurrentProcessId());
784 bool ret
= analyzeDebugData();
789 //---------------------------------------------------------------------------------------
790 void RosBootTest::dumpCheckpoints()
792 if (m_Checkpoints
.size ())
794 cerr
<< "Dumping list of checkpoints: "<< endl
;
795 while(!m_Checkpoints
.empty ())
797 cerr
<< m_Checkpoints
[0] << endl
;
798 m_Checkpoints
.erase (m_Checkpoints
.begin ());
803 //---------------------------------------------------------------------------------------
804 RosBootTest::DebugState
RosBootTest::checkDebugData(vector
<string
> & debug_data
)
806 /// TBD the information needs to be written into an provided log object
807 /// which writes the info into HTML/log / sends etc ....
810 DebugState state
= DebugStateContinue
;
812 for(size_t i
= 0; i
< debug_data
.size();i
++)
814 string line
= debug_data
[i
];
816 cout
<< line
<< endl
;
818 if (line
.find (RosBootTest::ROS_SYSREG_CHECKPOINT
) != string::npos
)
820 line
.erase (0, line
.find (RosBootTest::ROS_SYSREG_CHECKPOINT
) +
821 RosBootTest::ROS_SYSREG_CHECKPOINT
.length ());
822 if (!strncmp(line
.c_str (), m_Checkpoint
.c_str (), m_Checkpoint
.length ()))
824 state
= DebugStateCPReached
;
827 m_Checkpoints
.push_back (line
);
831 if (line
.find ("*** Fatal System Error") != string::npos
)
833 cerr
<< "Blue Screen of Death detected" <<endl
;
834 if (m_Checkpoints
.size ())
836 cerr
<< "dumping list of reached checkpoints:" << endl
;
839 string cp
= m_Checkpoints
[0];
840 m_Checkpoints
.erase (m_Checkpoints
.begin ());
842 }while(m_Checkpoints
.size ());
843 cerr
<< "----------------------------------" << endl
;
845 if (i
+ 1 < debug_data
.size () )
847 cerr
<< "dumping rest of debug log" << endl
;
848 while(i
< debug_data
.size ())
850 string data
= debug_data
[i
];
851 cerr
<< data
<< endl
;
854 cerr
<< "----------------------------------" << endl
;
856 state
= DebugStateBSODDetected
;
859 else if (line
.find ("Unhandled exception") != string::npos
)
861 if (m_CriticalImage
== "IGNORE")
864 /// ignoring all user-mode exceptions
870 if (i
+ 3 >= debug_data
.size ())
873 /// missing information is cut off -> try reconstruct at next call
880 /// extract address from next line
883 string address
= debug_data
[i
+2];
884 string::size_type pos
= address
.find_last_of (" ");
885 if (pos
== string::npos
)
887 cerr
<< "Error: trace is not available (corrupted debug info" << endl
;
892 address
= address
.substr (pos
, address
.length () - 1 - pos
);
895 /// extract module name
897 string modulename
= debug_data
[i
+3];
899 pos
= modulename
.find_last_of ("\\");
900 if (pos
== string::npos
)
902 cerr
<< "Error: trace is not available (corrupted debug info" << endl
;
906 string appname
= modulename
.substr (pos
+ 1, modulename
.length () - pos
);
907 if (m_CriticalImage
.find (appname
) == string::npos
&& m_CriticalImage
.length () > 1)
909 /// the application is not in the list of
910 /// critical apps. Therefore we ignore the user-mode
916 pos
= appname
.find_last_of (".");
917 if (pos
== string::npos
)
919 cerr
<< "Error: trace is not available (corrupted debug info" << endl
;
923 modulename
= appname
.substr (0, pos
);
925 cerr
<< "UM detected" <<endl
;
926 state
= DebugStateUMEDetected
;
932 result
.reserve (200);
934 SymbolFile::resolveAddress (modulename
, address
, result
);
935 cerr
<< result
<< endl
;
940 /// resolve frame addresses
948 if (clear
&& debug_data
.size () > 5)
954 //---------------------------------------------------------------------------------------
955 bool RosBootTest::analyzeDebugData()
963 if (m_DebugFile
.length ())
965 remove(m_DebugFile
.c_str ());
966 file
.open (m_DebugFile
.c_str ());
969 write_log
= file
.is_open ();
972 size_t prev_count
= vect
.size ();
973 if (!m_DataSource
->readSource (vect
))
979 for (size_t i
= prev_count
; i
< vect
.size (); i
++)
981 string
& line
= vect
[i
];
986 DebugState state
= checkDebugData(vect
);
987 if (state
== DebugStateBSODDetected
|| state
== DebugStateUMEDetected
)
992 else if (state
== DebugStateCPReached
)
996 lines
+= (vect
.size() -prev_count
); //WTF?
1005 } // end of namespace Sysreg_