Frequently Asked Questions (FAQ)
Answers to FAQ for CS343
The purpose of this material is to provide students with, or remind them of, the answers to Frequently-Asked Questions (FAQ) about course-related information, for example:
Do I need to read the whole FAQ?
Indeed not! Material in the FAQ is not part of the material for the course. However, in the interest of reducing question fatigue for the instructor and instructional coordinator, you should always look in the FAQ first for a possible answer to your question.
The course newsgroup Piazza cs343 is another source of information.
NOTE: Information of particular importance is preceded by "NOTE:".
Please send any corrections or suggestions to the course instructor(s).
Course Software (Courseware) and other Resources
- What compiler is used to complete assignments?
- Is there a PC version of μC++ for WINDOWS?
- Is there a PC version of μC++ for MAC OS X?
- Is there a PC version of μC++ for Linux?
- Does μC++ work with
valgrind
? - How to submit assignments?
- How to check assignment submissions?
- How to check number of lates?
- How to use
make
? - How to test if a
makefile
is set up correctly? - How to reset/change your password/shell?
- How to recover a file deleted or changed by accident?
- What to do with old files?
- How to keep files and reduce disk space?
- How to get account information, like disk quota?
- How to record shell output for assignments?
- How to use the course newsgroup?
- How to get UNIX help?
- Can you recommend a good C++ textbook?
Assignment Submission and Marking Policies
- What is the course late policy for assignments?
- How do I get an assignment or test remarked?
- May I use feature/technique X on the assignment?
- Can I change the interface for this routine/class/module?
- How do I share files when working in a group?
Problems or Errors with Courseware and Assignments
- How to report errors or things that do not work properly?
- Which include files do I have to use?
- How do I do stream I/O in C++?
- Which random number generator should I use?
- How do I dynamically create a matrix in C++?
- Can I perform
delete this
in an object's member routine? - How do I use the
string
class in C++? - How do I convert an
integer
to astring
in C++? - Why does
itoa
not work for integer-to-string conversions? - Why are floating point numbers not being saved in μC++?
- What does this C++ warning / error mean?
- What does this μC++ warning / error mean?
- How do I use gdb with μC++?
Course Software (Courseware) and other Resources
Various software, on-line documents, and services are provided to students to facilitate working on assignments. This section briefly summarizes these resources by answering the following questions:
- What compiler is used to complete assignments?
The GNU C++ compiler
g++
, and the μC++ translatoru++
are provided in the undergraduate environment for compiling assignments. Seeman gcc
or the C++ Tutorial for more details ong++
. Seeman u++
or the μC++ Annotated Reference Manual for more details onu++
. - Is there a PC version of μC++ for WINDOWS?
There is no port of μC++ to WINDOWS.
- Is there a PC version of μC++ for MAC OS X?
There is no port of μC++ to MAC OS X.
- Is there a Linux version of μC++?
- Does μC++? work with valgrind?
No μC++ does no work with valgrind because
valgrind
does not understand the coroutine/task stacks and the task threading. - How to submit assignments?
The
submit
command is provided to electronically submit assignments. Seeman submit
for more information. For example, to submit files in directorya1
for assignment1
enter:$ submit cs343 1 a1
which produces important messages describing its activities. Make sure you read these messages!
NOTE:
submit
for CS343 completely replaces your submission every time you use it, and all*txt
files greater than 250 lines in length are rejected.submit
tells you about both of these actions in its output messages.If you accidentally submit an incorrect file or multiple versions of a file, please remove it using the
-delete
option ofsubmit
. - How to check assignment submissions?
To check on a previous submission enter:
$ submit cs343 1 -list
which produces a listing of files previously submitted in a format similar to
ls -l
for the specified assignment.NOTE: try the
-list
option before decidingsubmit
has not worked properly. It is your responsibility to make sure all of your files have been submitted.Alternatively, this link gives a list of the files expected by submit in one column of a table, and gives a list of the files copied by submit to our
handin
directory in the other column, so you can compare what is submitted against what is expected. - How to check number of lates?
This link gives you a means of checking the number of lates you have left.
- How to use
make
?This link gives you basic information on
make
files, including some samplemakefile
s. - How to test if a
makefile
is set up correctly?This link gives you a means of checking that you are using the correct version of
make
, that yourmakefile
has been set up correctly, and that your submitted code compiles into the requested executable names. - How to reset/change your password/shell?
See instructions for resetting/changing your password.
- How to recover a file deleted or changed by accident?
In general, there is no way to do this. Once a file has been deleted (usually with
rm
) or altered and saved, the previous version is gone. However, you can attempt to deal with these accidents in two ways: recover an older version of the file from backup storage or prevent the accident in the first place.See restore for detailed information on retrieving a backup file. It is simplier than you think using
.snapshot
!You can attempt to prevent accidental file changes by using the
-i
option ofrm
when deleting files (i.e.,rm -i <files>
), or by making backup copies of your files before editing them (e.g.,cp <file> <file>.1
), or by archiving old copies of your files. - What to do with old files?
Delete them! Do not leave junk lying around. If you want or need to keep some of your old files, read about keeping files.
- How to keep files and reduce disk space?
If you want to keep a file or a directory containing a group of files for later reuse, then consider compressing it. A compressed file or directory contains the original file's or directory's contents in an encoded form that occupies less disk space. See
man zip
andman compress
for achieving and compressing a file or a directory of files. - How to get account information, like disk quota?
The amount of disk memory a user is permitted to occupy is called the disk quota. See MFCF's quota FAQ for detailed information on displaying quota information.
- How to record shell output for assignments?
The UNIX commands
script
andscriptfix
are available to make a record of shell activity. Thescript
program, once invoked, copies all the activity of a shell session into a file, which is handy for recording a sequence of commands and their results for later inspection. Therefore, thescript
command lends itself to debugging, testing, and reporting errors. A typical use ofscript
is given here:$ script foo
which makes a copy of all future shell activity in file
foo
. Theexit
command is used to end the script session.Since
script
records control characters (such as backspace) in their raw form, it is important to use thescriptfix
command to make the filefoo
clean and legible. Thescriptfix
program cleans up extraneous characters from a script file so it is readable. A typical use ofscriptfix
is given here:$ scriptfix foo > bar
which cleans the script file
foo
and redirects the result into the filebar
. Thescriptfix
command should always be run on script files that are submitted for assignments.NOTE:
scriptfix
does not remove DOS newlines. In order to remove these, use thedos2unix
utility. - How to use the course newsgroup?
The newsgroup Piazza cs343 is for the posting of information and questions relevant to the material covered in the course and for discussion of assignments. Generally, appropriate postings include informational messages from course staff, and error reports or information requests from students.
Other postings related to course material are also acceptable, but general discussions or personal requests should be carried on in other newsgroups, by email, or in person. A good rule of thumb is to make course material (e.g., assignments, class notes, etc.) the subject of your postings, rather than your own solutions or suggestions.
NOTE: students are expected to read the newsgroup regularly (usually every day) to stay current on any course-related developments. Doing so is solely the student's responsibility.
NOTE: there is an unfortunate tendency of newsgroup postings (and email messages) to become judgemental or to be interpreted uncharitably. This situation can result in so-called `flame wars' and unnecessarily aggressive posturing. Flaming is not tolerated in a course newsgroup! The most reasonable way to avoid this problem is to exercise patience when posting. "Is this posting really necessary?", "Is my phrasing clear and appropriate?", and "Would direct email be better than a public newsgroup?" are always good questions to ask yourself when regarding a potential posting. Finally, always read your posting twice before sending it.
- How to get UNIX help?
Normally, a good way to get help on some aspect of the UNIX operating system and shell commands is to read the
man
page for the particular feature or command. Manual pages related to a certain topic can be searched for with the-k
option of man (seeman man
for details).Also, MFCF has consultants in MC 3011 who can answer general questions and provide information about using UNIX in the undergraduate environment. You can also email them questions at
consultant@math
. See The Math Faculty Computing Facility for more details.NOTE: the consultants are NOT there to do your assignment for you!
- Can you recommend a good C++ textbook?
There is no single book that suits everyone's taste; it dependents on how you learn. For example, some people learn best with books that have lots of examples and explanations; others want only a reference book with just the facts and details. The point is that you must select a book that works for you. It is virtually impossible for someone else to do it.
You may also want to consider the text books recommended in CS 246.
Assignment Submission and Marking Policies
This section briefly outlines the policies for the CS343 course regarding submissions and remarking of submissions.
- What is the course late policy for assignments?
Normally, assignments are due at the time and date listed on the assignment document. At the instructor's discretion, N assignments may be handed in late up to D days after they are due. For N = D = 2, at most 2 assignments can be handed in 2 days late. For example, an assignment due on a Monday, has a late date on Wednesday. Lates cannot be combined on a single assignment. Should the late date be a University holiday, the late date is pushed to the next day the University is open. Assignments may not be due after the last day of lectures, so if an assignment due-date falls on the last day of lectures, there can be no late date. Any further late submissions are not accepted and a grade of 0 is assigned for that assignment. The
submit
command is configured to automatically enforce this policy. Requests for longer or more extensions may be granted provided the student provides a reason for not being able to complete the assignment on time (e.g., a note from a doctor).NOTE: Instructions on an assignment always take precedence over these rules, so be sure to check the assignment document carefully. It is solely the student's responsibility to comply with the requirements of any assignment.
- How do I get an assignment or exam remarked?
- Assignment
-
- Midterm Exam
-
The policy regarding midterm remarking is announced after the midterm is returned, and is similar to the assignment remarking.
- Final Exam
-
If you are unhappy with your final grade because your final-exam mark is lower than you expected, you must file a formal grade appeal to have your final exam remarked. You do this by going to the Math Undergrad Office and filling out a grade-appeal form.
- May I use feature/technique X on the assignment?
Never assume you can use a feature/technique in an assignment unless it has been discussed in class. In general, an assignment is a mechanism to practise some concept/idea/approach presented in class. Therefore, it is the purpose of the solution for an assignment to reflect this concept/idea/approach. Features/techniques discussed in class or explicitly specified in the assignment are usually the ones you are suppose to use. Solving the assignment using some other feature/technique does not provide the desired practise, even if the solution might be easier using the other feature/technique.
However, in many cases an assignment may involve the use of many features/techniques, some of which are crucial and some not. Therefore, it never hurts to ask the assignment coordinator, if a particular feature/technique is allowed. It is always better to ask, and ask early, so you do not have to redo parts of your assignment or are surprised at your assignment mark when it is returned.
- Can I change the interface for this routine/class/module?
NO! Interfaces provide the plugs that allow connecting parts of a program together. When you are given an interface you must respect it or other programs cannot plug into the service it provides. It does not matter that you are writing all the components of the program in an assignment, and hence, control all the interfaces. You must learn to develop programs from specifications, which often include specific interfaces. Hence, you can ask about altering an interface, but 99% of the time the answer will be NO.
- How do I share files when working in a group?
Working in a group usually requires sharing access to files. Sharing files must not involve sharing account passwords or making files readable/writable by everyone.
The best way to share files is through the Waterloo GitLab.
Problems or Errors with Courseware and Assignments
Problems with software for the course can and do occur. Also, problems occur when you are building software for your assignments. This section briefly summarizes some of these problems.
- How to report errors or things that do not work properly?
The proper reporting of potential software errors is important in solving problems and keeping the course progressing smoothly. Vague or incomplete error reports may simply frustrate both reporter and fixer. Please follow these general steps when reporting an error (whether electronically or in person):
-
Be sure that the error is really an error. Simply getting an error message does not mean something is broken. You may also find, in following the steps below, that the explanation of an error becomes apparent to you. In any case, the first step in reporting an error is ensuring you have considered all the relevant information. This often means:
- rereading the error message(s) carefully
- reconsidering the command invocation that produced the error
- verifying the outcome of the command invocation is indeed incorrect
- checking the assignment specification
- reading this FAQ to see if information on the error is available
- reading the newsgroup to see if information on the error has already been posted
- checking with the course notes or your class notes
- reading the appropriate
man
page(s)
This step should help you find the cause of errors and prevent trivial or ambiguous error reports.
-
If after checking all available resources the error remains, prepare an error report that reflects your efforts in the previous step. Often, this means sending a posting or email message that provides the following information:
- the conditions that hold when the error occurs (e.g., a directory listing, a code segment, the value of an environment variable, a quotation from an assignment)
- the EXACT command invocation(s) that produced the error
- the EXACT error message(s) produced by the command invocation(s)
- the reason you think the result obtained is incorrect
This information is important since diagnosing an error often requires being able to reproduce it. Reproducing requires knowing what conditions existed before the error, what command(s) produced the error, and why the result is incorrect. Without this information, the amount of guesswork in error diagnosis grows exponentially (well, it gets large).
NOTE: there is a natural tendency to `simplify' the reporting of difficulties by reporting a quasi-problem related to the real problem. If you do not know what the problem is, do not assume you can simplify it without losing important information! A common question from people who handle these difficulties is "what do you really want?"
NOTE: The
script
andscriptfix
commands are particularly well-suited to preparing error reports with courseware. -
Send the error report to the appropriate destination. Depending on the problem, this could mean emailing the TA, course tutor, instructor, posting to the course newsgroup or using the MFCF
gripe
command. If you happen to figure out the problem after you submit the error report, follow up with the information! Others may well be interested in what you have found out. This is important because, having followed the above steps, the error you reported must be non-trivial and the information should help to improve the course.
-
- Which include files do I have to use?
See Headers. Notice, the standard assumes include files without ".h" as suffix.
- Which random number generator should I use?
C routines
rand
andrandom
are simple and use a single lock for protection among multiple kernel-threads, so they are a shared point of contention and can slow a concurrent program significantly.Instead use, the μC++ pseudo random-number generators (PRNG) discussed in the μC++ Annotated Reference Manual, which have more features and excellent concurrent performance.
- How do I do stream I/O in C++?
There are example programs in the
examples
directory illustrating stream I/O in C++. - How do I dynamically create a matrix in C++?
See the C++ Tutorial for examples of creating and manipulating a matrix in C++.
- Can I perform
delete this
in an object's member routine?An object can commit suicide, i.e., it can "delete this", but it is a fragile operation: (from http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.14)
[16.14] Is it legal (and moral) for a member function to say delete this?
As long as you're careful, it's OK for an object to commit suicide (i.e.,
delete this
).Here's how I define "careful":
-
You must be absolutely 100% positive sure that this object was allocated via
new
(not bynew[]
, nor by placementnew
, nor a local object on the stack, nor a global, nor a member of another object; but by plain ordinarynew
). - You must be absolutely 100% positive sure that your member function will be the last member function invoked on this object.
-
You must be absolutely 100% positive sure that the rest of your member function (after the
delete this
line) doesn't touch any piece of this object (including calling any other member functions or touching any data members). -
You must be absolutely 100% positive sure that no one even touches the
this
pointer after thedelete this
line. In other words, you must not examine it, compare it with another pointer, compare it withNULL
, print it, cast it, do anything with it.
Naturally the usual caveats apply in cases where your this pointer is a pointer to a base class when you don't have a virtual destructor.
So yes it can be done, but it should only be done in special circumstances. Now the reason this works for objects is because the stack maintaining the routine calls is independent from the object allocation in the heap storage. Thus, it is possible to return from the object's member using the data on the stack even though the data for the object no longer exists.
HOWEVER, this last point is not true for a coroutine or task object because it has its own stack. Hence,
delete this
executed by a routine call on the coroutine/task stack fails because it causes both the object and stack to vanish, so the routine cannot continue execution on the deleted stack. Hence, the following axiom is true "you can commit suicide but not bury yourself". For an object with its own execution stack, it is always necessary to delete this kind of object on a stack different from the one that is being deleted. -
You must be absolutely 100% positive sure that this object was allocated via
- How do I use the
string
class in C++?There are example programs in the
examples
directory illustrating the use of thestring
class in C++. -
How do I convert an
integer
to astring
in C++?Unfortunately, the
string
type does not provide either an implicit or explicit conversion tointeger
, and therefore you have to write your own, e.g.:#include <sstream> #include <string> std::string itostring( long int i ) { std::stringstream ss; ss << i; return ss.str(); }
While converting from internal representation to a string is sometimes necessary, it should never be done to imitate Java style I/O in C++. For example, never do this:cout << "i = " + itostring( i ) + " j = " + itostring( 5 ) + "\n";
instead do this:cout << "i = " << i << " j = " << j << endl;
- Why does
itoa
not work for integer-to-string conversions?itoa
is not part of the ANSI C standard library. It is only implemented in some compilers. So, try something else, likestringstream
, orsprintf
. - Why are floating point numbers not being saved in μC++?
Floating-point registers are not saved by default during a context switch to reduce execution cost, particularly because most programs do not perform floating-point calculations. Therefore, each task using floating point has to explicitly indicate that the floating-point registers must be saved (see the μC++ Reference Manual on "User Specified Contexts" for details).
One exception is the SUN/SPARC architecture, which implicitly saves both the integer and floating point registers when a context switch occurs, but it is an anomaly among architectures. If you are running μC++ on a PCs or a non-SUN undergraduate machine, you have to explicitly indicate that the floating registers need to be saved for each task.
- What does this C++ warning / error mean?
The following is a list of some often-encountered warning/error-messages for C/C++ and the problems they typically indicate. Undoubtedly, not all the interesting ones are accounted for yet, so please feel free to request that other error messages be included here.
Compile-time Warning
-
test.cc: In constructor `B::B(int)': test.cc:11: warning: `B::i' will be initialized after test.cc:10: warning: `A B::a' test.cc:12: warning: when initialized here
C++ always initializes class members in the order they are declared not the order specified in the ctor-initializer of a constructor. For example, in:
#include <iostream> using namespace std; struct A { int i; A( int i ) : i( i ) {} }; struct B { A a; int i; B( int j ) : i( j ), a( i ) {} }; int main() { B b( 7 ); cout << b.i << " " << b.a.i << endl; }
the member variable "a" is initialized with an undefined value rather than the value of "j", even though the correct initialization ordering is specified in the constructor's ctor-initializer.
Compile-time Errors
-
ld: elf error ...
Check your diskquota to ensure you have enough space.
-
.../lib/uKernel-d.a(uC++-d.o): In function `uMain::uMain(int, char**, char**, int&, UPP::uAction)': .../src/kernel/uC++.cc:1978: undefined reference to `vtable for uMain' .../lib/uKernel-d.a(uC++-d.o): In function `uMain::~uMain()': .../src/kernel/uC++.cc:1983: undefined reference to `vtable for uMain'
A μC++ program must have a program
main
routine. For example, the above messages are generated for the following program, which is missing amain
routine:int fred() {}
-
test.cc:3: error: return type specification for constructor invalid
test.cc:4: error: return type specification for destructor invalid
A return type is not allowed on a constructor or destructor, e.g.:
class C { public: void C(); // no return type void ~C(); // no return type };
-
/bin/ld: /tmp/ccby7sXu.o: in function `main': test.cc:(.text+0x23): undefined reference to `C::C()' /bin/ld: test.cc:(.text+0x2f): undefined reference to `C::~C()'
Each prototype definition needs a corresponding implementation, e.g.:
class C { public: C(); // forget to define implementation ~C(); // forget to define implementation }; int main() { C c; // create instance without complete definition }
-
test.cc:3: error: `cout' undeclared (first use this function)
test.cc:3: error: `endl' undeclared (first use this function)
The named variables and routines have not been declared, i.e., violation of definition before use in C++. This problem often happens if there is a missing
#include
orusing
, e.g.:// forget to include <iostream.h> // forgot to import cout, endl int main() { cout << "abc" << endl; }
-
test.cc:4: error: uninitialized reference member `C::r'
For example, in:
class C { const int &r; public: C() {} };
the reference variable
r
must be initialized, but not at the point of declaration when declared in a class. Instead, initialization must occur by special syntax on each constructor, e.g.:class C { const int &r; public: C( int &rp ) : r( rp ) {} // special initialization syntax };
-
In file included from test.cc:2: T2.h:1: error: redefinition of `class T2' T2.h:1: error: previous definition of `class T2'
For example, in:
test.cc T1.h T2.h #include "T1.h" #include "T2.h" class T2 { #include "T2.h" class T1 { }; int main() { T2 t2; T1 t1; public: T2 t2; }; }
both
main
andT1
need to declare objects of typeT2
, so both includeT2.h
.main
also needs to declare objects of typeT1
, so it includesT1.h
. However, becauseT1.h
already includesT2.h
, the definition ofT2
is included twice intest.cc
. Either remove one of the includes (not always possible), or use the preprocessor#ifndef/#define/#endif
macros to ensure an include file is expanded only once even if it is included multiple times, e.g.:T2.h #ifndef __T2_H__ #define __T2_H__ class T2 { }; #endif
Runtime Errors
-
Bus Error/Segmentation Fault (core dumped)
is caused by a bad (invalid) memory address.
A bus error occurs when the bad memory address is outside the physical bounds of memory. For example:
int main() { int *ip = (int *)0xffffffff; // VERY LARGE memory address *ip += 1; // use the bad address }
the address
0xffffffff
exceeds the memory available on the computer.A segmentation error occurs when the bad address is within the physical bounds of memory but the address is not part of the user's program or is read-only memory such as for executable code. For example:
int main() { int *ip = (int *)0x0; // VERY small memory address *ip += 1; // use the bad address }
the address
0x0
is not writable because it contains executable code.Program errors that cause bus and segmentation errors are typically out-of-bounds array references and/or references through uninitialized or over-written pointers. One of the most common mistakes is to forget to allocate storage for a pointer variable, e.g.:
int main() { int *ip; // forget to "new" storage *ip += 1; // use the bad address }
uses the uninitialized value in pointer
ip
as a memory address, which may or may not cause an error.There are a number of methods for finding out where the program went wrong. One method is to use print statements to determine how far the program is getting before it crashes, and to print out the contents of interesting variables. A more sophisticated method is using a symbolic debugger such as gdb .
-
- What does this μC++ warning / error mean?
The following is a list of some often-encountered warning/error-messages for μC++ and the problems they typically indicate. These errors are generated by the μC++ translator not by the underlying C++ compiler. Undoubtedly, not all the interesting ones are accounted for yet, so please feel free to request that other error messages be included here.
Compile-time Errors
-
test.cc:1: uC++ Translator warning: unrecognizable text, possible uC++ syntax problem, skipping text to allow the C++ compiler to print an appropriate error. test.cc:1:6: error: cannot declare '::main' to be a global variable
For example, in:
int main {} // missing parenthesis, => main()
the parenthesis are missing after member routine
main()
, which confuses the μC++ translator. Subsequently, the C++ compiler prints the second error assuming the declaration is a variable rather than a routine. There are many syntax errors that can confuse both the μC++ translator and the C++ compiler. -
test.cc:(.text+0x2b): undefined reference to `T::T(UPP::uAction)' test.cc:(.text+0x44): undefined reference to `T::T(int, UPP::uAction)'
Each prototype definition needs a corresponding implementation, e.g.:
_Task T { void main() {} public: T(); // forget to implement T(int); // forget to implement }; int main() { T t1, t2(3); // create instances without complete definition }
This case is like the C++ one but notice the extra parameter
uAction
in the error message. This parameter is inserted by μC++ at the end of each constructor's parameter list and should be ignored when reading the error messages. -
test.cc:7:4: error: cannot declare variable 't' to be of abstract type 'T' test.cc:1:8: note: because the following virtual functions are pure within 'T': In file included from <command-line>:1:0: /usr/local/u++-7.0.0/inc/uC++.h:1819:15: note: virtual void uBaseTask::main()
Both a coroutine and a task definition must have a member named
main
defined, either directly or by inheritance, e.g.:_Task T { // no main member public: T() {} }; int main() { T t; }
-
test.cc:3: uC++ Translator error: accept on a nomutex member "mem", possibly caused by accept statement appearing before mutex-member definition.
For example, in:
_Task T { void main() { _Accept( mem ); } public: void mem() {} };
the accept of member
mem
appears before the definition of membermem
, and hence, the μC++ translator encounters the identifiermem
before it knows it is a mutex member. C++ requires definition before use in most circumstances. -
test.cc:7: uC++ Translator error: multiple accepts of mutex member "mem".
For example, in:
_Task T { public: void mem() {} private: void main() { _Accept( mem ); or _Accept( mem ); } };
the accept statement specifies the same member,
mem
, twice. The second specification is superfluous. -
test.cc:6: uC++ Translator error: accepting an invalid destructor; destructor name must be the same as the containing class "T2".
For example, in:
_Task T1 {}; _Task T2 { private: void main() { _Accept( ~T1 ); } };
the accept statement specifies the destructor from a different class,
T1
, within classT2
. -
test.cc:3: uC++ Translator error: derived type "C" of kind "COROUTINE" is incompatible with the base type "M" of kind "MONITOR"; inheritance ignored.
test.cc:4: uC++ Translator error: derived type "T1" of kind "TASK" is incompatible with the base type "C" of kind "COROUTINE"; inheritance ignored.
test.cc:5: uC++ Translator error: multiple inheritance disallowed between base type "M" of kind "MONITOR" and base type "C" of kind "COROUTINE"; inheritance ignored.
For example, in:
_Mutex class M {}; _Coroutine C : public M {}; _Task T1 : public C {}; _Task T2 : public M, public C {};
inheritance is restrictioned among kinds of types in μC++ (see the μC++ Reference Manual on "Inheritance" for details).
-
test.cc:3: uC++ Translator error: "T" redeclared with different kind.
For example, in:
_Task T; // prototype _Coroutine T {}; // definition
the kind of type for the prototype,
_Task
, does not match the kind of type for the definition,_Coroutine
. -
test.cc:4: uC++ Translator error: multiple inheritance disallowed between base type "M1" of kind "MONITOR" and base type "M2" of kind "MONITOR"; inheritance ignored.
For example, in:
_Mutex class M1 {}; _Mutex class M2 {}; _Mutex class M3 : public M1, public M2 {}; // multiple inheritance
only one base type can be a mutex type when inheriting.
-
test.cc:6: uC++ Translator error: mutex attribute of "T::mem" conflicts with previously declared nomutex attribute.
For example, in:
_Task T { public: _Nomutex void mem(); }; _Mutex void T::mem() {}
the kind of mutual exclusion,
_Nomutex
, for the prototype ofmem
, does not match the kind of mutual exclusion,_Mutex
, for the definition. -
test.cc:4: uC++ Translator error: constructor must be mutex, nomutex attribute ignored.
test.cc:6: uC++ Translator error: destructor must be mutex for mutex type, nomutex attribute ignored.
For example, in:
_Task T { public: _Nomutex T() {} // must be mutex _Nomutex ~T() {} // must be mutex };
a constructor of a mutex type must be mutex because the thread of the constructing task is active in the object, and a destructor must be mutex if it is a member of a mutex type because deletion requires mutual exclusion.
-
test.cc:2: uC++ Translator error: cannot create anonymous coroutine or task because of the need for named constructors and destructors.
For example, in:
_Task /* no name */ {};
a type without a name cannot have constructors or destructors since both are named after the type, and the μC++ translator needs to generate constructors and destructors if not present for certain kinds of types.
-
test.cc:1: error: 'uSemphore' does not name a type
For example, in:
uSemphore s;
the type
uSemaphore
is undefined. To access the type includeuSemaphore.h
.
Runtime Warnings
-
uC++ Runtime warning (UNIX pid:4823) : program terminating with 1024(0x400) bytes of storage allocated but not freed. Possible cause is unfreed storage allocated by the program or system/library routines called from the program.
This warning can (usually) be ignored. For example, in:
#include <string> using std::string; int main() { string s = "abc"; }
this message indicates unfreed storage, but it does not imply the storage is allocated by the user's code. Many system (e.g., exceptions) and library (e.g.,
string
type and socket I/O) operations allocate storage (such as buffers) for the duration of the program, and therefore, there is little reason to free the storage at program termination. (Why clean up and then terminate?) There is nothing that can be done about this unfreed storage. Therefore, the value printed is only a guide in determining if all of a user's storage is freed.What use is this message? Any sudden increase of unfreed storage from some base value may be a strong indication of unfreed storage in the user's program. For example, one of the most common mistakes in freeing memory is to incorrectly delete dynamically-allocated arrays, as in:
int *intArray = new int[N]; delete intArray; // must be: delete [] intArray
Always follow the rule that if the
new
has[]
so must thedelete
. If the[]
are forgotten when deleting an array, C++ only deletes the first element of the array, thinking there is only one item to delete notN
. The remainingN-1
elements become unfreed storage.What if this warning message offends you? Is there anything that can be done to eliminate it? NO! Just ignore it and you will not lose marks. DO NOT use
exit(0)
at the end of your program to remove the message because global destructors are not executed, i.e., the program is stopped immediately without proper cleanup. DO NOT compile your program with the-nodebug
flag to remove the message because this eliminates many useful runtime error checks. Marks will be deducted if you take either of these steps to eliminate this message.In general, do not worry about this message if you have freed all of the storage you allocated.
-
/usr/local/u++-5.0.1/src/library/uSocket.cc:817: the use of `tempnam' is dangerous, better use `mkstemp'
This warning can be ignored. It occurs only on Linux systems due to an over zealous and paranoid implementor of
mkstemp
. The usage oftempnam
in μC++ is correct and it cannot be replaced bymkstemp
. If this message bothers you, please contact the implementor ofmkstemp
and ask them to remove the message as it is incorrect in certain cases.
Runtime Errors
-
uC++ Runtime error (UNIX pid:143271) Propagation failed to find a matching handler. Possible cause is a missing try block with appropriate catch clause for specified or derived exception type, or throwing an exception from within a destructor while propagating an exception. Type of last active exception: int Error occurred while executing task main (0x7ffd0b5b3280).
For example, in:
int main() { throw 1; }
no
try
statement with an appropriatecatch
clause is in effect so propagation fails to locate a matching handler. -
uC++ Runtime error (UNIX pid:14239) terminate called without an active exception Error occurred while executing task main (0x7ffc3af3b560).
For example, in:
int main() { throw; // rethrow }
because a rethrow must occur in a context with an active (already raised) exception so that exception can be raised again.
-
uC++ Runtime error (UNIX pid:4258) (uSerial &)0x670c00 : Entry failure while executing mutex destructor: mutex object has been destroyed. Error occurred while executing task main (0x7fff75a95e40).
For example, in:
_Monitor M { public: void mem() {} }; int main() { M *m = new M; delete m; // delete storage m->mem(); // make call to mutex member }
task
main
deletes the monitorm
and then calls the member routinemem
through the deleted pointer. As a result, taskmain
finds the mutex object has been destroyed. -
uC++ Runtime error (UNIX pid:117381) (uSerial &)0x13e8cf8 : Entry failure while executing mutex destructor: task main (0x7fffa136fb30) found blocked on acceptor/signalled stack. Error occurred while executing task T2 (0x1428e08).
For example, in:
_Task T1 { uCondition w; public: void mem() { w.wait(); } private: void main() { _Accept( mem ); // let T2 in so it can wait w.signal(); // put T2 on acceptor/signalled stack _Accept( ~T1 ); // task main is calling the destructor } }; _Task T2 { T1 &t1; void main() { t1.mem(); } public: T2( T1 &t1 ) : t1( t1 ) {} }; int main() { T1 *t1 = new T1; T2 *t2 = new T2( *t1 ); delete t1; // delete in same order as creation delete t2; }
task
t2
is allowed to wait on condition variablew
int1.mem
, and then taskt1
signals conditionw
, which moves taskt2
to the acceptor/signalled stack, and accepts its destructor. As a result, when taskmain
attempts to delete taskt1
, it finds taskt2
still blocked on the acceptor/signalled stack. -
uC++ Runtime error (UNIX pid:22780) (uSerial &)0x1e91cf8 : Entry failure while executing mutex destructor: task main (0x7ffc08d9e250) found blocked on entry queue. Error occurred while executing task T2 (0x1ed1e08).
For example, in:
_Task T1 { public: void mem() {} private: void main() { _Accept( ~T1 ); } }; _Task T2 { T1 &t1; public: T2( T1 &t1 ) : t1( t1 ) {} private: void main() { t1.mem(); } }; int main() { T1 *t1 = new T1; T2 *t2 = new T2( *t1 ); delete t1; delete t2; }
task
t2
happens to block on the call tot1.mem
, and then taskt1
accepts its destructor. As a result, when taskmain
attempts to delete taskt1
, it finds taskt2
still blocked on the entry queue oft1
. -
uC++ Runtime error (UNIX pid:76504) Attempt by waiting task main (0x7fffd4950370) to release mutex lock currently owned by task no-owner ((nil)). Error occurred while executing task main (0x7fffd4950370).
For example, in:
uOwnerLock m; uCondLock c; int main() { c.wait( m ); // does not own lock => cannot release it to block }
task
main
waits on conditionc
and attempts to release owner lockm
as part of blocking. However, taskmain
cannot release a lock it does not own. -
uC++ Runtime error (UNIX pid:20191) (uCondition &)0x253cda8 : Waiting failure as task main (0x7ffdebb2abe0) found blocked task T2 (0x257ce08) on condition variable during deletion. Error occurred while executing task T2 (0x257ce08).
For example, in:
_Task T1 { uCondition w; public: void mem() { w.wait(); } private: void main() { _Accept( mem ); } }; _Task T2 { T1 &t1; void main() { t1.mem(); } public: T2( T1 &t1 ) : t1( t1 ) {} }; int main() { T1 *t1 = new T1; T2 *t2 = new T2( *t1 ); delete t1; delete t2; }
the call to
t1.mem
blocks taskt2
on condition queuew
and then taskt1
implicitly accepts its destructor when itsmain
terminates. As a result, when taskmain
attempts to delete taskt1
, it finds taskt2
still blocked on the condition queue. -
uC++ Runtime error (UNIX pid:21135) (uBaseCoroutine &)0x7ffe95b03e80 : Unhandled exception in coroutine main raised non-locally from resumed coroutine C (0x7ff5f0afb8c0), which was terminated due to an unhandled thrown exception of type E. Error occurred while executing task main (0x7ffe95b03e80).
For example, in:
_Event E {}; _Coroutine C { void main() { _Throw E(); } public: void mem() { resume(); } }; int main() { C c; c.mem(); // first call fails }
the call to
c.mem
resumes coroutinec
and then coroutinec
throws an exception it does not handle. As a result, when the top ofc
's stack is reached, an exception of typeuBaseCoroutine::UnHandledException
is raised at coroutinemain
, since it last resumedc
. -
uC++ Runtime error (UNIX pid:9042) (uBaseCoroutine &)0x7ffcb2d7b480 : Unhandled exception in coroutine main raised non-locally from coroutine C1 (0x7fbd5803b8c0), which was terminated due to a series of unhandled exceptions -- originally an unhandled thrown exception of type E inside coroutine C2 (0xf0e810). Error occurred while executing task main (0x7ffcb2d7b480).
For example, in:
_Event E {}; _Coroutine C2 { void main() { _Throw E(); } public: void mem() { resume(); } }; _Coroutine C1 { void main() { C2 c2; c2.mem(); } public: void mem() { resume(); } }; int main() { C1 c1; c1.mem(); // first call fails }
the call to
c1.mem
resumes coroutinec1
, which creates coroutinec2
and call toc2.mem
to resume it, and then coroutinec2
throws an exception it does not handle. As a result, when the top ofc2
's stack is reached, an exception of typeuBaseCoroutine::UnHandledException
is raised at coroutinemain
, since it last resumedc
. -
uC++ Runtime error (UNIX pid:22689) Attempt by coroutine main (0x7fff3bfa9960) to resume terminated coroutine C (0x7f8ff33bb8c0). Possible cause is terminated coroutine's main routine has already returned. Error occurred while executing task main (0x7fff3bfa9960).
For example, in:
_Coroutine C { void main() {} public: void mem() { resume(); } }; int main() { C c; c.mem(); // first call works c.mem(); // second call fails }
the first call to
c.mem
resumes coroutinec
and then coroutinec
terminates. As a result, when coroutinemain
attempts the second call toc.mem
, it finds coroutinec
terminated. A similar situation can be constructed usingsuspend
, but is significantly more complex to generate. -
uC++ Runtime error (UNIX pid:9792) Attempt to suspend coroutine C (0x7f5693fae8c0) that has never been resumed. Possible cause is a suspend executed in a member called by a coroutine user rather than by the coroutine main. Error occurred while executing task main (0x7ffd6ed9b2e0).
For example, in:
_Coroutine C { void main() {} public: void mem() { suspend(); // suspend before any resume } }; int main() { C c; c.mem(); }
the call to
C::mem
executes a suspend before the coroutine'smain
member is started, and hence, there is no resumer to reactivate. In general, membersuspend
is only called within the coroutine main or non-public members called directly or indirectly from the coroutine main, not in public members called by other coroutines. -
uC++ Runtime error (UNIX pid:13194) Attempt by task T (0x7f6cc9d646b0) to resume coroutine C (0x7f6cc9d642a0) currently being executed by task T (0x7f6cc9d643a0). Possible cause is two tasks attempting simultaneous execution of the same coroutine. Error occurred while executing task T (0x7f6cc9d646b0).
Two tasks cannot simultaneously execute the same coroutine; only one task can use the coroutine's execution at a time. For example, in:
_Coroutine C { void main() { uBaseTask::yield(); } public: void mem() { resume(); } }; _Task T { C &c; void main() { c.mem(); } public: T( C &c ) : c( c ) {} }; int main() { C c; T t1( c ), t2( c ); }
because
t1
's thread first calls routineC::mem
and then resumes coroutinec
, where it yields the processor.t2
's threads now calls routineC::mem
and attempts to resume coroutinec
butt1
is currently usingc
's execution-state (stack). -
uC++ Runtime error (UNIX pid:10925) Stack overflow detected: stack pointer 0x21b60a0 below limit 0x2236000. Possible cause is allocation of large stack frame(s) and/or deep call stack. Error occurred while executing task main (0x7ffea3716780).
uC++ provides no support for automatic growth of stack space for coroutines and tasks. Several checks are made to mitigate problems resulting from lack of dynamic stack growth. For example, in:
int main() { char x[1000 * 1024]; // array larger than stack space uThisTask().verify(); }
the declaration of the array in task
main
uses more than the current stack space (see the μC++ Reference Manual for details on adjusting the stack size). -
uC++ Runtime error (UNIX pid:18114) Attempt to perform a non-nested entry and exit from multiple accessed mutex objects. Error occurred while executing task T (0x1c5ed80).
It is a restriction that a task must acquire and release mutex objects in nested (LIFO) order. For example, in:
_Task T; _Cormonitor CM { T *t; void main(); public: void mem( T *t ) { // task owns mutex object CM::t = t; resume(); // begin coroutine main } }; _Task T { CM &cm; void main() { cm.mem( this ); // call coroutine monitor } public: T( CM &cm ) : cm( cm ) {} void mem() { resume(); // restart task in CM::mem } }; void CM::main() { t->mem(); // call back into task } int main() { CM cm; T t( cm ); }
t
's thread first calls mutex routineCM::mem
(and now owns coroutine monitorcm
) and then resumes coroutinecm
, which now calls the mutex routineT::mem
(t
already owns itself). The coroutinecm
resumest
from withinT::mem
, which restarts inCM::mem
(full coroutining) and exits before completing the nested call to mutex routineT::mem
(wherecm
is suspended). Therefore, the calls to these mutex routines do not terminate in LIFO order. -
uC++ Runtime error (UNIX pid:14492) Attempt by task T (0x1ea9d80) to activate coroutine C (0x1ea9940) currently executing in a mutex object owned by task T (0x1ea9a50). Possible cause is task attempting to logically change ownership of a mutex object via a coroutine. Error occurred while executing task T (0x1ea9d80).
For example, in:
_Task T; _Coroutine C { T *t; void main(); public: void mem( T *t ) { C::t = t; resume(); } }; _Task T { C &c; void main() { c.mem( this ); yield(); } public: T( C &c ) : c( c ) {} void mem() { resume(); } }; void C::main() { t->mem(); } int main() { C c; T t1( c ), t2( c ); }
because
t1
's thread first calls routineC::mem
and then resumes coroutinec
, which now calls the mutex routineT::mem
.t1
restarts inC::mem
and returns back toT::main
and yields the processor.t2
's threads now calls routineC::mem
and attempts to resume coroutinec
, which would restartt2
viac
inT::mem
. However, this resumption would result in a logical change in ownership becauset2
has not acquired ownership oft1
. -
uC++ Runtime error (UNIX pid:30268) Attempt by task main (0x7fffe99ae8e0) to call the destructor for uSerial 0x21acaf8, but this task has outstanding nested calls to this mutex object. Possible cause is deleting a mutex object with outstanding nested calls to one of its members. Error occurred while executing task main (0x7fffe99ae8e0).
For example, in:
class T; _Monitor M { public: void mem( T *t ); }; class T { M *m; public: void mem1() { m = new M; // allocate object m->mem( this ); // call into object } void mem2() { delete m; // delete object with pending call } }; void M::mem( T *t ) { t->mem2(); // call back to caller } int main() { T t; t.mem1(); }
it is incorrect storage-management to delete any object if there are outstanding nested calls to the object's members. This case is only detected for mutex objects.
-
uC++ Runtime error (UNIX pid:25711) Attempt by task T (0x1b8dda0) to call the destructor for uSerial 0x1b92ad8, but this destructor was already called by task main (0x7ffe704d9e80). Possible cause is multiple tasks simultaneously deleting a mutex object. Error occurred while executing task T (0x1b8dda0).
For example, in:
_Monitor M { uCondition w; public: ~M() { w.wait(); // force deleting task to wait } }; _Task T { M *m; void main() { delete m; // delete mutex object } public: T( M *m ) : m(m) {} }; int main() { M *m = new M; // create mutex object T t( m ); // create task delete m; // also delete mutex object }
it is incorrect to perform more than one delete on a mutex object, which can happen if multiple tasks attempt to perform simultaneous deletes on the same object. This case is only detected for mutex objects.
-
uC++ Runtime error (UNIX pid:32712) Attempt to delete task T (0x269cd90) that is not halted. Possible cause is task blocked on a condition queue. Error occurred while executing task main (0x7fff66f5c940).
The destructor of a task cannot execute if the thread of that task has not finished (halted) because the destructor destroys the environment in which the task's thread is executing. For example, in:
_Task T { uCondition w; void main() { _Accept( ~T ); // task main invokes destructor w.wait(); // T continues but blocks, which restarts task main } }; int main() { T t; } // implicitly invoke T::~T
the call to the destructor restarts the accept statement, and the thread of
t
blocks on conditionw
, which restarts the destructor. However, the destructor cannot cleanup without invalidating any subsequent execution of taskt
. -
uC++ Runtime error (UNIX pid:8783) Attempt to wait on a condition variable for a mutex object not locked by this task. Possible cause is accessing the condition variable outside of a mutex member for the mutex object owning the variable. Error occurred while executing task T (0x1b64da0).
Only the owner of a condition variable can wait and signal on it. For example, in:
_Task T { uCondition &w; void main() { w.wait(); } public: T( uCondition &w ) : w( w ) {} }; int main() { uCondition w; T t( w ); w.wait(); }
the condition variable
w
is passed from taskmain
tot
, and then there is a race to wait on the condition. The error message shows that taskmain
waited first, and hence, became the condition owner, and thent
's attempt to wait fails. Changingwait
inT::main
tosignal
generates a similar message with respect to signalling a condition not owned by mutex objectt
. It is possible for one mutex object to create a condition and pass it to another, as long as the creator does not wait on it before passing it. -
uC++ Runtime error (UNIX pid:20556) Attempt to signal a condition variable for a mutex object not locked by this task. Possible cause is accessing the condition variable outside of a mutex member for the mutex object owning the variable. Error occurred while executing task main (0x7fff70e938e0).
A signal or wait can only occur in a mutex member, since the signal and wait statement access internal data structures of the mutex object (e.g., condition variable, entry queue and acceptor/signalled stack). Therefore, all operations on these data structures must be performed with mutual exclusion. For example, in:
_Task T { uCondition w; void main() { w.wait(); } public: _Nomutex void mem() { w.signal(); } }; int main() { T t; uThisTask().yield(); t.mem(); }
Changing
signal
inT::mem
towait
generates a similar message with respect to waiting on a condition not locked by mutex objectt
. -
uC++ Runtime error (UNIX pid:17936) Attempt to access user data on an empty condition. Possible cause is not checking if the condition is empty before reading stored data. Error occurred while executing task main (0x7fff56ca99f0).
A condition variable must be non-empty before examining data stored with the front task blocked on the queue. For example, in:
int main() { uCondition w; int i = w.front(); }
the condition variable
w
is empty, so there is no data to return. -
uC++ Runtime error (UNIX pid:22496) Attempt to accept in a mutex object not locked by this task. Possible cause is accepting in a nomutex member routine. Error occurred while executing task main (0x7ffd1e23b9c0).
For example, in:
_Monitor M { public: void mem1() {} _Nomutex void mem2() { _Accept( mem1 ); // not allowed in non-mutex member } }; int main() { M m; m.mem2(); }
an accept statement is executed in a non-mutex member.
-
uC++ Runtime error (UNIX pid:31328) Attempt to delete cluster userCluster (0x1568200) with task T (0x1629a88) still on it. Possible cause is the task has not been deleted. Error occurred while executing task uBootTask (0x651ce0).
A cluster cannot be deleted with a task still on it, regardless of what state the task is in (i.e., blocked, ready or running). For example, in:
_Task T { void main() {} }; int main() { T *t = new T; }
the
uBootTask
happens to delete the user cluster after taskmain
terminates and before the dynamically allocated taskt
has terminated. Deleting the task associated witht
before taskmain
terminates solves the problem. -
uC++ Runtime error (UNIX pid:10716) Attempt to free storage 0x1 with address outside the heap. Possible cause is duplicate free on same block or overwriting of memory. Error occurred while executing task main (0x7ffe57250640).
For example, in:
int main() { int *ip = (int *)1; // invalid pointer address delete ip; }
the value of pointer
ip
is not within the heap storage area, and therefore, cannot be deleted. -
uC++ Runtime error (UNIX pid:13382) Attempt to free storage 0xc62a9c with address outside the heap. Possible cause is duplicate free on same block or overwriting of memory. Error occurred while executing task main (0x7fffacd12680).
or
uC++ Runtime error (UNIX pid:26076) Alignment 4294967294 for memory allocation is less than 16 and/or not a power of 2. Error occurred while executing task program main (0x7fffffffe530).
For example, in:
int main() { int *ip = new int[10]; delete &ip[5]; // not the start of the array }
the pointer passed to
delete
must always be the same as the pointer returned fromnew
. In this case, the value passed todelete
is in the middle of the array instead of the start. -
uC++ Runtime error (UNIX pid:4917) Attempt to free storage 0x961c00 with corrupted header. Possible cause is duplicate free on same block or overwriting of header information. Error occurred while executing task main (0x7fff2af797d0).
For example, in:
int main() { int * ip = new int; delete ip; delete ip; // duplicate free }
the pointer is freed twice without an intervening allocation.
-
uC++ Runtime error (UNIX pid:4159) (uSerial &)0x2815cf8 : Entry failure while executing mutex destructor: mutex object has been destroyed. Error occurred while executing task main (0x7ffda1d89410).
For example, in:
_Task T { void main() {} public: void mem() {} }; int main() { T *t = new T; delete t; t->mem(); // use deleted storage }
an attempt is made to use the storage for task
t
after it is deleted, which is always incorrect. This storage may have been reallocated to another task and now contains completely different information. The problem may be detected inside of the μC++ kernel, where there are assertion checks for invalid pre or post conditions. Using storage incorrectly can trigger other "internal errors" from the μC++ kernel, e.g.:uC++ Runtime error (UNIX pid:2670) (uSpinLock &)0x92a50.acquire() : internal error, attempt to multiply acquire spin lock by same task. Error occurred while executing task main (0xffbef008).
-
uC++ Runtime error (UNIX pid:31257) No ready or pending tasks. Possible cause is tasks are in a synchronization or mutual exclusion deadlock. Error occurred while executing task uProcessorTask (0xf863a8) in coroutine uProcessorKernel (0xec5cf0).
For example, in:
#include <uSemaphore.h> int main() { uSemaphore s(0); s.P(); // block only thread => synchronization deadlock }
the only thread blocks so there are no other tasks to execute, resulting in a synchronization deadlock. This message also appears for the more complex form of deadlock resulting from mutual exclusion.
-
uC++ Runtime error (UNIX pid:121462) Null pointer (nullptr) dereference. Error occurred while executing task main (0x7fff8d2ab330).
For example, in:
int main() { int *ip = nullptr; // set address to 0 *ip += 1; // use the bad address }
the value of pointer
ip
is probably within the executable code, which is read only, but an attempt to write is occurring. This case is like the C++ one but μC++ provides a more informative message before terminating the program. -
uC++ Runtime error (UNIX pid:21810) Application interrupted by a termination signal. Error occurred while executing task main (0x7fff5f3ce0c0).
If a uC++ program is looping for some reason, it may be necessary to terminate its execution. Termination is accomplished using a shell
kill
command, sending signalSIGTERM
to the UNIX process. uC++ receives the termination signal and attempts to shutdown the application, which is important in multikernel mode with multiple processors. For example, in:#include <unistd.h> // getpid prototype int main() { kill( getpid(), SIGTERM ); // send SIGTERM signal to program }
the μC++ program sent itself a termination (
SIGTERM
) signal.
-
- How do I use gdb with μC++?
The symbolic debugging tools (e.g.,
dbx
,gdb
) do not work perfectly with μC++, because each coroutine and task has its own stack, and the debugger does not know about these stacks. When a program terminates with an error, only the stack of the coroutine or task in execution at the time of the error is understood by the debugger. During execution, the debugger cannot step through a context switch for either a coroutine or task. Therefore, it is necessary to put break points after the suspend/resume or signal/wait to acquire control, and just continue execution through the context switch. Once the breakpoint is reached, it is possible to next/step through the lines of the coroutine/task until the next context switch. For debuggers that handle multiple kernel threads (corresponding to μC++ virtual processors), it is possible to examine the active task running on each kernel thread.(gdb) info threads # list all kernel threads (gdb) thread 2 # switch to kernel thread 2
Finally, it is necessary to tell the debugger that μC++ is handling UNIX signals
SIGALRM
andSIGUSR1
to perform pre-emptive scheduling. For gdb, the following debugger commands allows the application program to handle signalSIGALRM
andSIGUSR1
:(gdb) handle SIGALRM nostop noprint pass ignore (gdb) handle SIGUSR1 nostop noprint pass ignore