Exception handling capabilities

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Exception handling capabilities

Post by jean »

I'm developing a small C++ library that heavily uses inheritance and polymorphinsm, and depends upon exceptions implementation. I'm not using std in any way (i'm not satisfied of it on certain technical and license aspects) but a quick look into some source code made me aim my attention on -fno-exceptions flag (that -if i understood well- makes code workaround the lack of hw exceptions handling background with a serie of if-like structures). Speaking of PSP-specific code, i noticed -fno-exceptions is ALWAYS set in all sources i inspected. Why? And: is PSP technically capable of handling exceptions "the classical way"??
Thanks for your attention
jean
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

the lack of hw exceptions ?

no, -fno-exceptions option has nothing about hardware exceptions. This is merely two different definitions indeed. c++ exceptions are used through "try/catch".

if I'm not wrong, to have c++ exception handling (with RTTI for dynamic_cast as well), you need to provide specifically some files with run-time functions to handle c++ exceptions properly. I guess we use -fno-exceptions because those files are probably not adapted so they could work on PSP.

To be frank, I'm not sure having -fno-exception(s) would prevent you from using "try/catch" (using for instance a simple setjmp/longjmp version) because I never tried so
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

sorry, that "hw" before "exception" was not intended...it escaped from my fingers :) !! (i was to mean the "traditional" built-in way of handling exceptions) In fact the only thing i care is performance when making heavy use of software try..catch that surely has nothing to do with hw exception like a cpu issued "div by zero" (even if visual c++ seems to merge the two things in a weird manner...) Are you basically saying "if you don't try, you'll never know" ?
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

jean wrote:Are you basically saying "if you don't try, you'll never know" ?
kinda :)

I'm not sure a lot of us were paying any attention about -fno-exceptions -no-rtti and were using them because they were already set in template or sample Makefiles.

EDIT:

"try/catch" are accepted when compiled but there is no special generated code to handle c++ exception (as if they were comments). However, "throw" is refused : "error: exception handling disabled, use -fexceptions to enable"
pspZorba
Posts: 156
Joined: Sat Sep 22, 2007 11:45 am
Location: NY

Post by pspZorba »

I'm not sure a lot of us were paying any attention about -fno-exceptions -no-rtti and were using them because they were already set in template or sample Makefiles.
actually generally rtti slows the exe and makes it fatter. And the cases in which you really need them are rather rare.

and even if try/catch is more usefull, unfortunatly it makes the code unreadable (IMHO). That's probably why nobody never change those options.
--pspZorba--
NO to K1.5 !
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

That's probably why nobody never change those options.
Ok, I'll try to use -fexceptions ....
Heimdall
Posts: 245
Joined: Thu Nov 10, 2005 1:29 am
Location: Netherlands
Contact:

Post by Heimdall »

Any success with exception handling? I'm trying to get it work but aparently it just hangs, here's the code:

Code: Select all

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>

/* Define printf, just to make typing easier */
#define printf  pspDebugScreenPrintf

/* Define the module info section */
PSP_MODULE_INFO&#40;"cppException", 0, 1, 1&#41;;

/* Define the main thread's attribute value &#40;optional&#41; */
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | THREAD_ATTR_VFPU&#41;;

#include <exception>
using namespace std;

class myexception&#58; public exception
&#123;
	virtual const char* what&#40;&#41; const throw&#40;&#41;
	&#123;
		return "My exception happened";
	&#125;
&#125; myex;

int main &#40;void&#41;
&#123;
	pspDebugScreenInit&#40;&#41;;
	SceCtrlData pad;
	
	try
	&#123;
		throw myex;
	&#125;
	catch &#40;exception& e&#41;
	&#123;
		printf&#40;"%s\n", e.what&#40;&#41;&#41;;
	&#125;
	
	printf&#40;"\nPress X to quit.\n"&#41;;

	for &#40;;;&#41;
	&#123;
		sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
		if &#40;pad.Buttons & PSP_CTRL_CROSS&#41;
			break;
	&#125;
	sceKernelExitGame&#40;&#41;;

	return 0;
&#125;
Am i doing something wrong?
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

Since then, i used exceptions extensively and without issues... Don't know, maybe your makefile... let me look into an old project of mine....
makefile:

Code: Select all

TARGET = SERIAL_ROBOT
OBJS = main.o Command.o GetMotorCommand.o GetSensorCommand.o MotorPacket.o Packet.o PacketFactory.o RcvGetMotorPacket.o RcvGetSensorPacket.o SerialComm.o serialPacketReaderMachine.o SetMotorCommand.o SndGetMotorPacket.o SndGetSensorPacket.o SndSetMotorPacket.o sioDriver.o callbacks.o MotorController.o

BUILD_PRX=1
PSP_FW_VERSION=390
INCDIR =
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $&#40;CFLAGS&#41; -fexceptions -fno-rtti  -D "__PSP"
ASFLAGS = $&#40;CFLAGS&#41;

LIBS = -lstdc++

LIBDIR =
LDFLAGS = 

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = SERIAL_ROBOT

PSPSDK=$&#40;shell psp-config --pspsdk-path&#41;
include $&#40;PSPSDK&#41;/lib/build.mak 
i specified my own type of base exception, never inherited from std::exception

exceptions.hpp:

Code: Select all

#ifndef __Exceptions_H__
#define __Exceptions_H__

/// \brief RException class.
class RException
&#123;
&#125;;

/// \brief CommInitException class.
class CommInitException&#58; public RException
&#123;
&#125;;

/// \brief WrongMachineStateException class.
class WrongMachineStateException&#58; public RException
&#123;
&#125;;
// ...etc...

#endif // __Exceptions_H__

Anyhow, where does the code hang? You can easily track the exact point setting some printf(); around: if it does hang on throw, try to write mujltiple catch statements ending with the (...) one just to make that code not hang, then you can dig the reason why you con't catch that type.

Code: Select all

try
&#123;
   // throwing code
&#125;
catch &#40;specializedException1 e&#41;
&#123;
  // ...
&#125;
catch &#40;specializedException2 e&#41;
&#123;
  // ...
&#125;
catch &#40;baseException e&#41;
&#123;
  // ...
&#125;
catch &#40;...&#41; // you can never know what can happen...
&#123;
  // ...
&#125;
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

If i'm not wrong, you need to provide a c++ exception handling implementation for PSP in gcc source. Gcc can generate calls to some functions and you need to implement them as they may be dependent on OS. As far as i know psp-gcc has no such implementation.
Heimdall
Posts: 245
Joined: Thu Nov 10, 2005 1:29 am
Location: Netherlands
Contact:

Post by Heimdall »

that was what i thought hlide, and started to add a simple threading implementation to gcc based on an old haiku-os implementation that only tels gcc that this device can handle mutexes, however when i throw an exception the mutex locks everything (i guess i got it all wrong from the gcc barely existing docs).

I've reverted my patch and exceptions work out of the box like jean says both with base user defined exception or using the sdt::exception one.

I've tested with gcc 4.3.3 and newlib 1.16 / gcc 4.3.3 and newlib 1.17
User avatar
Torch
Posts: 825
Joined: Wed May 28, 2008 2:50 am

Post by Torch »

Can someone explain exactly how to set this up and get try-catch working for a normal makefile?
Heimdall
Posts: 245
Joined: Thu Nov 10, 2005 1:29 am
Location: Netherlands
Contact:

Post by Heimdall »

This is my test cpp file:

Code: Select all

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>

/* Define printf, just to make typing easier */
#define printf  pspDebugScreenPrintf

/* Define the module info section */
PSP_MODULE_INFO&#40;"cppException", 0, 1, 1&#41;;

/* Define the main thread's attribute value &#40;optional&#41; */
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | HREAD_ATTR_VFPU&#41;;

#include <exception>
using namespace std;

class myexception&#58; public exception
&#123;
	virtual const char* what&#40;&#41; const throw&#40;&#41;
	&#123;
		return "My exception happened";
	&#125;
&#125; myex;

int main &#40;void&#41;
&#123;
	pspDebugScreenInit&#40;&#41;;
	SceCtrlData pad;
	
	try
	&#123;
		throw myex;
	&#125;
	catch &#40;exception& e&#41;
	&#123;
		printf&#40;"%s\n", e.what&#40;&#41;&#41;;
	&#125;
	
	printf&#40;"\nPress X to quit.\n"&#41;;
	for &#40;;;&#41;
	&#123;
		sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
		if &#40;pad.Buttons & PSP_CTRL_CROSS&#41;
			break;
	&#125;
	sceKernelExitGame&#40;&#41;;

	return 0;
&#125;
and this is my makefile:

Code: Select all

TARGET = CppException
OBJS = main.o
LIBS = -lstdc++

INCDIR =
CFLAGS = -G0 -Wall -O2
CXXFLAGS = $&#40;CFLAGS&#41; -fno-rtti -fexceptions
SFLAGS = $&#40;CFLAGS&#41;

LIBDIR =
LDFLAGS =

XTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = C++ Exception Sample

PSPSDK=$&#40;shell psp-config --pspsdk-path&#41;
include $&#40;PSPSDK&#41;/lib/build.mak
the the usual, make, copy eboot and run :) on the makefile the only difference is that i define -fexceptions instead of -fno-exceptions on the CXXFLAGS variable, and obviously link againts -lstdc++ because this is a cpp sample.

PS: I'm adding this sample to the SDK in my builds.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

Heimdall wrote:that was what i thought hlide, and started to add a simple threading implementation to gcc based on an old haiku-os implementation that only tels gcc that this device can handle mutexes, however when i throw an exception the mutex locks everything (i guess i got it all wrong from the gcc barely existing docs).

I've reverted my patch and exceptions work out of the box like jean says both with base user defined exception or using the sdt::exception one.

I've tested with gcc 4.3.3 and newlib 1.16 / gcc 4.3.3 and newlib 1.17
so you can use -fexception but not rtti (dynamic_cast<> unusable). If a bus error occurs, do you catch a C++ exception ?
Heimdall
Posts: 245
Joined: Thu Nov 10, 2005 1:29 am
Location: Netherlands
Contact:

Post by Heimdall »

hlide wrote:so you can use -fexception but not rtti (dynamic_cast<> unusable). If a bus error occurs, do you catch a C++ exception ?
It works for user defined exceptions that are programatically thrown, meaning all classes that extend std::exception (or not, like jean example ) can be throw and catched. For bus errors I haven't tested those thoroughly. For those I need to have at least mutexes working, but my gcc code failed:

Code: Select all

static inline int
__gthread_mutex_lock &#40;__gthread_mutex_t * mutex&#41;
&#123;
  int stat;
  int thid;
  int own;
  int rtn;

  if &#40;mutex->semid < 0&#41;
  &#123;
    mutex->semid = sceKernelCreateSema &#40;"libgccMutex", 0x0100, 1, 1, 0&#41;;
  &#125;

  thid = sceKernelGetThreadId &#40;&#41;;
  stat = sceKernelCpuSuspendIntr &#40;&#41;;
  if &#40;mutex->owner < 0&#41;
  &#123;
    mutex->owner = thid;
    own = 1;
  &#125;
  else
    own = 0;
  sceKernelCpuResumeIntr &#40;stat&#41;;

  if &#40;own || mutex->owner != thid&#41;
  &#123;
    rtn = sceKernelWaitSema &#40;mutex->semid, 1, 0&#41;;
  &#125;
  else
  &#123;
    mutex->count++;
    rtn = 0;
  &#125;
    
  return rtn;
&#125;

static inline int
__gthread_mutex_trylock &#40;__gthread_mutex_t * mutex&#41;
&#123;
  int stat;
  int thid;
  int own;
  int rtn;

  if &#40;mutex->semid < 0&#41;
  &#123;
    mutex->semid = sceKernelCreateSema &#40;"libgccMutex", 0x0100, 1, 1, 0&#41;;
  &#125;

  thid = sceKernelGetThreadId &#40;&#41;;
  stat = sceKernelCpuSuspendIntr &#40;&#41;;
  if &#40;mutex->owner < 0&#41;
  &#123;
    mutex->owner = thid;
    own = 1;
  &#125;
  else
    own = 0;
  sceKernelCpuResumeIntr &#40;stat&#41;;

  if &#40;own || mutex->owner != thid&#41;
  &#123;
    rtn = sceKernelPollSema &#40;mutex->semid, 1&#41;;
  &#125;
  else
  &#123;
    mutex->count++;
    rtn = 0;
  &#125;
    
  return rtn;
&#125;

static inline int
__gthread_mutex_unlock &#40;__gthread_mutex_t * mutex&#41;
&#123;
  int rtn;

  if &#40;mutex->owner == sceKernelGetThreadId &#40;&#41;&#41;
  &#123;
    if &#40;mutex->count > 0 && --mutex->count == 0&#41;
    &#123;
      rtn = sceKernelSignalSema &#40;mutex->semid, 1&#41;;
      mutex->owner = -1;
      sceKernelDeleteSema &#40;mutex->semid&#41;;
      mutex->semid = -1;
    &#125;
  &#125;
  else if &#40;mutex->owner >= 0&#41;
  &#123;
    rtn = sceKernelSignalSema &#40;mutex->semid, 1&#41;;
    mutex->owner = -1;
    sceKernelDeleteSema &#40;mutex->semid&#41;;
    mutex->semid = -1;
    mutex->count = 0;
  &#125;
  else
    rtn = -1;

  return rtn;
&#125;
I tryed to get it working like they did on the Haiku project before they had a pthread implementation... but this is wrong, because this will cause a hand on every throw clause.

Maybe I understood the semaphore api wrong or passing the wrong arguments either...[/code]
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

if you need a mutex, why not using those :
sceKernelCreateMutex
sceKernelDeleteMutex
sceKernelLockMutex
sceKernelLockMutexCB
sceKernelTryLockMutex
sceKernelUnlockMutex
Heimdall
Posts: 245
Joined: Thu Nov 10, 2005 1:29 am
Location: Netherlands
Contact:

Post by Heimdall »

i guess you're right, i didn't look for the mutex api, and it contains what gcc needs :) I'll try that...
Heimdall
Posts: 245
Joined: Thu Nov 10, 2005 1:29 am
Location: Netherlands
Contact:

Post by Heimdall »

hlide wrote:if you need a mutex, why not using those :
sceKernelCreateMutex
sceKernelDeleteMutex
sceKernelLockMutex
sceKernelLockMutexCB
sceKernelTryLockMutex
sceKernelUnlockMutex
humm... those functions are only available in the SDK or DA SDK meaning I don't know the method signature... more work to do...
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

Heimdall wrote:
hlide wrote:if you need a mutex, why not using those :
sceKernelCreateMutex
sceKernelDeleteMutex
sceKernelLockMutex
sceKernelLockMutexCB
sceKernelTryLockMutex
sceKernelUnlockMutex
humm... those functions are only available in the SDK or DA SDK meaning I don't know the method signature... more work to do...
ok,

Code: Select all

SceUID sceKernelCreateMutex&#40;
  const char *name,
  unsigned int attributes, /* using a FIFO &#40;0&#41; or PRIORITIZED &#40;256&#41; THREAD QUEUE */
  int initial_count, /* the initial count of mutex when created */
  void *optional_parameters /* just pass 0 */ 
&#41;; // returns Mutex UID if >0

int sceKernelDeleteMutex&#40;
  SceUID id /* Mutex UID */
&#41;; // returns 0 if OK

int sceKernelLockMutex&#40;
  SceUID id, /* Mutex UID */
  int lock_count, /* > 0 */
  unsigned int *timeout /* In &#58; 0 if no timeout or a pointer to a placeholder containing the max time value in ms. Out&#58; time remaining when mutex is acquired */
&#41;; // returns 0 if OK or 0x800201a8 if timeout

int sceKernelLockMutexCB&#40;
  SceUID id, /* Mutex UID */
  int lock_count, /* > 0 */
  unsigned int *timeout /* In &#58; 0 if no timeout or a pointer to a placeholder containing the max time value in ms. Out&#58; time remaining when mutex is acquired */
&#41;; // returns 0 if OK or 0x800201a8 if timeout

int sceKernelTryLockMutex&#40;
  SceUID id, /* Mutex UID */
  int lock_count /* > 0 */
&#41;; // returns 0x800201c4 if current thread failed to own mutex or 0 if success

int sceKernelUnlockMutex&#40;
  SceUID id, /* Mutex UID */
  int unlock_count /* >= 0 */
&#41;; // returns 0 if OK
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

Ok let's imagine we have a kernel prx wich installs a exception handler and that we can call a handler individually for each different cause :

void exc_install_handler(int cause, void (*handler)(int, exc_t *exc));

Basically, the general exception we installed through a kernel prx will be called when an exception is raised. This exception will call the right handler in an array where the cause is index. The previous function exported by the kernel prx allows us to define an handler per cause : bus error, FPU error, etc.

Now, we can define some C++ exceptions this way :

Code: Select all

 class HardwareException
 &#123;
 public&#58;
     HardwareException&#40;exc_t *exc&#41;
     &#123;
         m_exc = exc;
     &#125;
 protected&#58;
     exc_t m_exc;     
 &#125;;

 template <class ExceptionClass> class ExceptionTranslator
 &#123;
 private&#58;
     class SingletonTranslator
     &#123;
     public&#58;
         SingletonTranslator&#40;&#41;
         &#123;
             exc_install_handler&#40;ExceptionClass&#58;&#58;cause&#40;&#41;, handler&#41;;
         &#125;

         static void handler&#40;int, exc_t *exc&#41;
         &#123;
             throw ExceptionClass&#40;exc&#41;;
         &#125;
     &#125;;

 public&#58;
     ExceptionTranslator&#40;&#41;
     &#123;
         static SingletonTranslator s_obj_Translator;
     &#125;
 &#125;;

 // An example for BUS_ERROR
class SegmentationFault &#58; public HardwareException, public exception
 &#123;
 public&#58;
     SegmentationFault&#40;exc_t *exc&#41; &#58; HardwareException&#40;exc&#41;, exception&#40;&#41; &#123;&#125;
     static int cause&#40;&#41; &#123; return BUS_ERROR; &#125;
     ...
 &#125;;

 ExceptionTranslator<SegmentationFault> g_obj_SegmentationFaultTranslator;

 // An example for FPU_ERROR
 class FloatingPointException &#58; public HardwareException, public exception
 &#123;
 public&#58;
     FloatingPointException&#40;exc_t *exc&#41; &#58; HardwareException&#40;exc&#41;, exception&#40;&#41; &#123;&#125;
     static int cause&#40;&#41; &#123; return FPU_ERROR; &#125;
     ...
 &#125;;

 ExceptionTranslator<FloatingPointException> g_obj_FloatingPointExceptionTranslator;
coolkehon
Posts: 355
Joined: Mon Oct 20, 2008 5:44 am

Post by coolkehon »

sorry to intrude on this post but how can i use c++ in a kernel prx i have been having a problem with that for the longest it never compiles
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

huh this is just an example to show how we could handle C++ exception to throw when an hardware exception occurs (access to null pointer, divide error, etc.) which are not catchable with the actual psp-gcc.

I think if we could tweak the source of exception.prx (not sure about the name) which is a homebrew kernel prx to allow a user application to catch every hardware exception and dump the details with a blue screen of death.

I think we can modify it so we can add an array of exception handlers and an exported function to install a user handler to call. Nothing is in C++.

The code I posted in C++ shows how in our c++ application we can try to make hardware exceptions turn into c++ exception. To do so, we could load this kernel prx and import such a function to install a user handler per hardware exception category to call the right "throw".

This is just an idea for those who are interested with.
Last edited by hlide on Tue Mar 10, 2009 10:45 pm, edited 1 time in total.
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

very very nice...thank you for sharing: will surely use something like this.
Post Reply