Ticket #154 (closed bug: fixed)

Opened 6 years ago

Last modified 5 years ago

subclassed ExceptionHandlers can't handle exceptions

Reported by: cotto Owned by: whiteknight
Priority: major Milestone:
Component: core Version: trunk
Severity: medium Keywords: exception, subclass, exceptionhandler
Cc: Language:
Patch status: Platform: all

Description

Trying to use a subclassed ExceptionHandler causes a segfault. From the backtrace it looks like something's getting stuck in an infinite loop:

#1  0xb7b1939a in Parrot_vsprintf_s (interp=0x804f040, pat=0x81502e8, args=0xbf6b3f40 "�_�,\213\b\b�6m�\002") at src/misc.c:69
#2  0xb7b194f3 in Parrot_vsprintf_c (interp=0x804f040, pat=0xb7e25af8 "%s() not implemented in class '%Ss'", 
    args=0xbf6b3f40 "�_�,\213\b\b�6m�\002") at src/misc.c:91
#3  0xb7af32d6 in build_exception_from_args (interp=0x804f040, ex_type=37, format=0xb7e25af8 "%s() not implemented in class '%Ss'", 
    arglist=0xbf6b3f40 "�_�,\213\b\b�6m�\002") at src/exceptions.c:265
#4  0xb7af39bc in Parrot_ex_throw_from_c_args (interp=0x804f040, ret_addr_unused=0x0, exitcode=37, 
    format=0xb7e25af8 "%s() not implemented in class '%Ss'") at src/exceptions.c:401
#5  0xb7c6fbe4 in cant_do_method (interp=0x804f040, pmc=0x80e8588, methname=0xb7e25fc3 "elements") at ./src/pmc/default.pmc:65
#6  0xb7c7115e in Parrot_default_elements (interp=0x804f040, pmc=0x80e8588) at ./src/pmc/default.c:824
#7  0xb7d41c62 in Parrot_ExceptionHandler_nci_can_handle (interp=0x804f040, pmc=0x80c4888) at ./src/pmc/exceptionhandler.pmc:201
#8  0xb7cb6fb2 in Parrot_NCI_invoke (interp=0x804f040, pmc=0x80c4888, next=0x0) at ./src/pmc/nci.pmc:321
#9  0xb7b0a596 in Parrot_PCCINVOKE (interp=0x804f040, pmc=0x80e79a0, method_name=0x807ad20, signature=0xb7e20df7 "P->I")
    at src/inter_call.c:2709
#10 0xb7b5b1c5 in Parrot_cx_find_handler_local (interp=0x804f040, task=0xb66d36e0) at src/scheduler.c:795
#11 0xb7af36d8 in Parrot_ex_throw_from_c (interp=0x804f040, exception=0xb66d36e0) at src/exceptions.c:299
#12 0xb7af39d1 in Parrot_ex_throw_from_c_args (interp=0x804f040, ret_addr_unused=0x0, exitcode=37, 
    format=0xb7e25af8 "%s() not implemented in class '%Ss'") at src/exceptions.c:404
#13 0xb7c6fbe4 in cant_do_method (interp=0x804f040, pmc=0x80e8588, methname=0xb7e25fc3 "elements") at ./src/pmc/default.pmc:65
#14 0xb7c7115e in Parrot_default_elements (interp=0x804f040, pmc=0x80e8588) at ./src/pmc/default.c:824

<snip>

#7524 0xb7af39d1 in Parrot_ex_throw_from_c_args (interp=0x804f040, ret_addr_unused=0x0, exitcode=37,
    format=0xb7e25af8 "%s() not implemented in class '%Ss'") at src/exceptions.c:404
#7525 0xb7c6fbe4 in cant_do_method (interp=0x804f040, pmc=0x80e8588, methname=0xb7e25fc3 "elements") at ./src/pmc/default.pmc:65
#7526 0xb7c7115e in Parrot_default_elements (interp=0x804f040, pmc=0x80e8588) at ./src/pmc/default.c:824
#7527 0xb7d41c62 in Parrot_ExceptionHandler_nci_can_handle (interp=0x804f040, pmc=0x80c4888) at ./src/pmc/exceptionhandler.pmc:201
#7528 0xb7cb6fb2 in Parrot_NCI_invoke (interp=0x804f040, pmc=0x80c4888, next=0x0) at ./src/pmc/nci.pmc:321
#7529 0xb7b0a596 in Parrot_PCCINVOKE (interp=0x804f040, pmc=0x80e79a0, method_name=0x807ad20, signature=0xb7e20df7 "P->I")
    at src/inter_call.c:2709
#7530 0xb7b5b1c5 in Parrot_cx_find_handler_local (interp=0x804f040, task=0x80e74c0) at src/scheduler.c:795
#7531 0xb7af36d8 in Parrot_ex_throw_from_c (interp=0x804f040, exception=0x80e74c0) at src/exceptions.c:299
#7532 0xb7af39d1 in Parrot_ex_throw_from_c_args (interp=0x804f040, ret_addr_unused=0x0, exitcode=37,
    format=0xb7e25af8 "%s() not implemented in class '%Ss'") at src/exceptions.c:404
#7533 0xb7c6fbe4 in cant_do_method (interp=0x804f040, pmc=0x80e8588, methname=0xb7e25fc3 "elements") at ./src/pmc/default.pmc:65
#7534 0xb7c7115e in Parrot_default_elements (interp=0x804f040, pmc=0x80e8588) at ./src/pmc/default.c:824
#7535 0xb7d41c62 in Parrot_ExceptionHandler_nci_can_handle (interp=0x804f040, pmc=0x80c4888) at ./src/pmc/exceptionhandler.pmc:201
#7536 0xb7cb6fb2 in Parrot_NCI_invoke (interp=0x804f040, pmc=0x80c4888, next=0x0) at ./src/pmc/nci.pmc:321
#7537 0xb7b0a596 in Parrot_PCCINVOKE (interp=0x804f040, pmc=0x80e79a0, method_name=0x807ad20, signature=0xb7e20df7 "P->I")
    at src/inter_call.c:2709
#7538 0xb7b5b1c5 in Parrot_cx_find_handler_local (interp=0x804f040, task=0x80e7628) at src/scheduler.c:795
#7539 0xb7af3a58 in Parrot_ex_throw_from_op (interp=0x804f040, exception=0x80e7628, dest=0x812eb8c) at src/exceptions.c:198
#7540 0xb7abc77c in Parrot_throw_p (cur_opcode=0x812eb84, interp=0x804f040) at src/ops/core.ops:853
#7541 0xb7b5ac80 in runops_slow_core (interp=0x804f040, pc=0x812eb84) at src/runops_cores.c:228
#7542 0xb7b10769 in runops_int (interp=0x804f040, offset=0) at src/interpreter.c:984
#7543 0xb7b11151 in runops (interp=0x804f040, offs=0) at src/inter_run.c:108
#7544 0xb7b11541 in runops_args (interp=0x804f040, sub=0x80e8ba0, obj=0x80bdcf0, meth_unused=0x0, sig=0xb7e1b973 "vP",
    ap=0xbfeb1f0c "\210\213\016\bX\037�@<���\004\b�\213\016\b\210\213\016\b\b|�X\037뿬\003߷@�\004\b\001") at src/inter_run.c:248
#7545 0xb7b12406 in Parrot_runops_fromc_args (interp=0x804f040, sub=0x80e8ba0, sig=0xb7e1b973 "vP") at src/inter_run.c:315
#7546 0xb7aefd67 in Parrot_runcode (interp=0x804f040, argc=1, argv=0xbfeb2088) at src/embed.c:984
#7547 0xb7df03ac in imcc_run_pbc (interp=0x804f040, obj_file=0, output_file=0x0, argc=1, argv=0xbfeb2088) at compilers/imcc/main.c:824
#7548 0xb7df0fc5 in imcc_run (interp=0x804f040, sourcefile=0xbfeb3347 "subclass_eh_sf.pir", argc=1, argv=0xbfeb2088)
    at compilers/imcc/main.c:1116
#7549 0x08048988 in main (argc=1, argv=0xbfeb2088) at src/main.c:61

The code to duplicate the behavior is very simple. I just pared down t/pmc/exceptionhandler.t, which already had a note about the bug.

.sub main :main

    .local pmc myhandler, myeh

    myhandler = subclass ['ExceptionHandler'], ['MyHandler']
    #myhandler = get_class ['ExceptionHandler']

    new myeh, myhandler
    set_addr myeh, subclassed_handler
    push_eh myeh

    $P0 = new ['Exception']
    throw $P0

  subclassed_handler:
    say "caught an ex"
.end

Change History

  Changed 6 years ago by whiteknight

I've been doing some digging into this issue this morning, and I have some information:

1) The infinite loop is caused because we're having a problem with the current exception handler, which causes an exception to get thrown, which looks at the current handler, which has a problem, which causes an exception to get thrown... Internally, if we get into a situation where we could potentially be having a problem with the current exception handler, we should definitely not throw another exception to signal that.

2) On the handler PMC, we try to invoke the "can_handle" method. For some reason the subclassed PMC doesn't seem to have a "can_handle" method (Which I thought it should have inherited from the ExceptionHandler PMC). This defaults to src/pmc/default.pmc:cant_do_method, which throws the exception to enter the infinite loop. I tried to add an explicit "can_handle" method in PIR, but this causes a different error (which I am digging into):

.namespace ["MyHandler"]

.sub "can_handle" :method
  .param pmc e
  return(1)
.end

This causes an assertion failure and a backtrace, not an infinite loop and a segfault.

I've got a few ideas here about how to fix this, I'll report back if I can find anything that works.

follow-up: ↓ 3   Changed 6 years ago by jkeenan

Is this related to the peculiar test result I reported about 12 hours earlier in TT #151?

in reply to: ↑ 2   Changed 6 years ago by jkeenan

Replying to jkeenan:

Is this related to the peculiar test result I reported about 12 hours earlier in TT #151?

TT #151 has cleared up, though I don't know why.

  Changed 6 years ago by NotFound

The subclass with an explicit can_handle method used to work.There is a test for it in t/pmc/exceptionhandler.t, wich I wrote. Now is skipped without any comment and the reason is that 'causes segfault'. Very good, we just hide the problems instead to fix or at least minimally document them. Congratulations.

  Changed 6 years ago by coke

For other commiters out there, if you happen to notice that a skip message references a bug is happening but doesn't list a ticket number, and you happen to know the bug id, please add one. If you can't find a bug id, please open a ticket and refer to the skipped test. That would be most helpful.

Added reference to this ticket to the test in question in r35875

Segfault confirmed as still occurring in r35863.

  Changed 6 years ago by NotFound

After several fixes and hacks, in r36409 the test does not segfault, it gives a non misleading error message. However it can not be unskipped yet because the hack against infinite recursion avoids the 'todo' call.

The problem now is that the proxy'ed object is not able to read the ATTR in the parent PMC. I think this is a generic problem in class derived from PMC, not just in the ExceptionHandler particular case.

  Changed 6 years ago by NotFound

Unskipped the test in r36467 by adding a excpetion handler that catches the second exception, after r36463 change that modifies the handling of nested throws.

The solution is hackish, but at least can help to better diagnose the problems.

  Changed 6 years ago by NotFound

Dropping the todo in r36528, after pmc attribute inheritance changes allows this to work.

  Changed 6 years ago by whiteknight

  • owner set to whiteknight
  • status changed from new to assigned

So just to double-check the current status, the PMC attribute inheritance work is now allowing this test to pass without an infinite loop and a segfault? If so, doesn't that mean that this ticket is resolved?

  Changed 6 years ago by cotto

The original example is still failing for me, although now it's with an assertion failure and a backtrace rather than a segfault. Adding the can_handle snippet from whiteknight's first reply makes the example run. I'd expect the subclass of ExceptionHandler to inherit can_handle from its parent. Unless there's a reason that such inheritance shouldn't happen, once it's working this ticket can be closed.

  Changed 6 years ago by whiteknight

Okay, I think I have this issue figured out. Our subclassed type inherits from ExceptionHandler, which in turn inherits from Continuation PMC. Continuation has a Parrot_Context ATTR which cannot be inherited by a PIR subclass. Only INTVAL, FLOATVAL, PMC* and STRING* I think can be inherited. When we try to use the subclassed handler, we call

if (PMC_cont(handler)->current_results)

The normal GET_ATTR_* macros would throw an exception here that we tried to access an ATTR from a subclass that could not get accessed from a PIR subclass. However, the PMC_cont macro does not throw such an exception (that would have helped us identify the problem much earlier). When we go to pass the results, somebody is using the {{{PMC_cont}} macro and causing the problems that we see.

The good news: This should be fixable but we will have to add in a hackish special case in Parrot_ex_throw_from_op to deal with PIR subclasses. I'll try to work on that tonight.

The better news: This problem should disappear entirely once we've turned Contexts into PMCs. See r596 for detail about that project.

There are several tickets like this: Where a PIR subclass of a built-in PMC type cannot properly inherit methods, and I think we should check the inheritance tree for non-inheritable ATTRs that might be the problem.

  Changed 6 years ago by whiteknight

I committed a small partial fix in r38884. Now the example code provided in the description of this ticket works. However, this fix is just a quick hack and is not very robust. Specifically, I do not think that you can use .get_results to get access to the exception object that was thrown.

Needs testing and improving.

  Changed 5 years ago by coke

  • status changed from assigned to closed
  • resolution set to fixed

This code (which includes the original sample and poking in the exception once in the handler) works as of 41185

.sub main :main

    .local pmc myhandler, myeh

    myhandler = subclass ['ExceptionHandler'], ['MyHandler']
    #myhandler = get_class ['ExceptionHandler']

    new myeh, myhandler
    set_addr myeh, subclassed_handler
    push_eh myeh

    $P0 = new ['Exception']
    $P0['message'] = 'a message'
    throw $P0

  subclassed_handler:
    get_results '0', $P0
    print "subclassed handler: "
    $S0 = $P0['message']
    say $S0
.end
Note: See TracTickets for help on using tickets.