Falcon 0.9.6.8 segfaults

74 views
Skip to first unread message

garthy...@entropicsoftware.com

unread,
Apr 27, 2013, 8:11:56 AM4/27/13
to falc...@googlegroups.com
Hi,

I've just started experimenting with embedding Falcon (I'm using 0.9.6.8- latest stable), under Debian Linux 6 (64-bit), and I've run into a few problems.

Firstly, I'm trying to obtain a class from a loaded file. Using "findGlobalSymbol" and "findGlobalItem" doesn't find it, although using the main live module and "findModuleItem" does work. However, if I get this item, confirm it is callable, and attempt to call it ("callItem"), I get an immediate segfault. The docs suggest I should be able to call a class (and it presumbly creates an instance of it), and "isCallable()" suggests it should be callable, so I'm not quite sure what I am doing wrong.

Also, attempts to call "findLocalSymbolItem" or "findLocalVariable" segfault immediately.

I seem to have found a workaround, which is to include a function in the script that creates an object of that class. This seems to work, but the ideal would be to be able to create an object by class name, not require a creation function for each class.

I've put together some test code to go through these scenarios, see the end of this post for the code.

Test #0 just initialises things, and does little else. This runs to completion.
Test #1 tried to obtain the class and call it, which segfaults.
Test #2 uses the workaround I mentioned. This runs to completion.
Test #3 uses findLocalSymbolItem, which segfaults.
Test #4 uses findLocalVariable, which segfaults.

"run" runs all five tests.

Am I going about things the right way? What would be the correct way to obtain a class and create a new object from it? Does this code work for other people, is it just crashing on my system?

Secondly, I believe there may be a bug relating to the decision of when to regenerate "*.fam" files. My guess is that if the last-written time of the "fal" file is after the "fam" file, it is regenerated. Unfortunately, there are cases where the "fal" file may have been updated since that are missed by this, such as if you save a changed "fal" file during or too close to generation of the "fam" file. A quick test to show the problem is given in "tic" below, which rapidly creates and runs ten different Falcon scripts. The output should be of the form "Run x: Hello y." for x from 0 to 9, which x and y equal, but on my machine at least, this isn't usually true (usually y == 0 for all x). I'm guessing the fix might just be a case of needing to change a "<" to a "<=" somewhere.

Anyway, I hope this information is useful, and I'm definitely keen to hear any suggestions re the first problem at least.

Cheers,
Garth

=============================================================================
=== run
=============================================================================

#!/bin/sh
make || exit 1
for test in 0 1 2 3 4; do
  echo "=================== Test ${test}"
  ./uncall ${test}
done

=============================================================================
=== Makefile
=============================================================================

FALCON_PREFIX=/path/to/falcon
FALCON_INCLUDE=$(FALCON_PREFIX)/include/falcon0.9.6

uncall: uncall.o
    g++ -o uncall uncall.o -L$(FALCON_PREFIX)/lib -lfalcon_engine -lm

uncall.o: uncall.cpp
    g++ -Wall -o uncall.o -I$(FALCON_INCLUDE) -c uncall.cpp

clean:
    rm -f uncall uncall.o core.* *.fam *~

=============================================================================
=== uncall.cpp
=============================================================================

#include <falcon/engine.h>
#include <falcon/vm.h>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#define CLASS_NAME "MyClass"

using namespace std;

static int test = 0;

int main(int argc, char *argv[])
{
  try
  {
    if (argc >= 2)
      test = atoi(argv[1]);
   
    cerr << "Running test " << test << ".\n";
   
    Falcon::Engine::Init();
    Falcon::ModuleLoader *loader = new Falcon::ModuleLoader();
    Falcon::Runtime *runtime = new Falcon::Runtime(loader);
    Falcon::VMachine *vm = new Falcon::VMachine;
    vm->link(Falcon::core_module_init());
   
    unlink("uncall.fam");
   
    runtime->loadFile("uncall.fal");

    vm->link(runtime);
    vm->launch();
 
    if ((test == 1) || (test == 2))
    {
      const Falcon::SymModule *sym = vm->findGlobalSymbol(CLASS_NAME);
      cerr << "VM Global symbol " << (sym ? "FOUND" : "NOT FOUND") << "\n";
      const Falcon::Item *item = vm->findGlobalItem(CLASS_NAME);
      cerr << "VM Global Item " << (item ? "FOUND" : "NOT FOUND") << "\n";
   
      Falcon::LiveModule *lmodule = vm->mainModule();
      if (lmodule)
      {
        cerr << "Found live module.\n";
        const Falcon::Item *item = NULL;
       
        if (test == 1)
          item = lmodule->findModuleItem(CLASS_NAME);
        else
          item = lmodule->findModuleItem("create_" CLASS_NAME);
        if (item)
        {
          cerr << "Found module item.\n";
          if (item->isCallable())
          {
            cerr << "Item is callable.\n";
            cerr << "Calling now...\n";
            vm->callItem(*item, 0);
            cerr << "Success.\n";
          }
        }
      }
    }
   
    if (test == 3)
    {
      cerr << "About to call findLocalSymbolItem.\n";
      Falcon::Item *item = vm->findLocalSymbolItem(CLASS_NAME);
      cerr << "Success.\n";
      cerr << "Result " << (item ? "FOUND" : "NOT FOUND") << "\n";
    }

    if (test == 4)
    {
      Falcon::Item item;
      cerr << "About to call findLocalVariable.\n";
      bool found = vm->findLocalVariable(CLASS_NAME, item);
      cerr << "Success.\n";
      cerr << "Result " << (found ? "FOUND" : "NOT FOUND") << "\n";
    }
  }
  catch (Falcon::Error *err)
  {
    Falcon::AutoCString edesc(err->toString());
    string msg(edesc.c_str());
    err->decref();
    cerr << "Falcon Error: " << msg << "\n";
  }
 
  cerr << "Normal exit.\n";
 
  //Falcon::Engine::Shutdown();
  return 0;
}

=============================================================================
=== uncall.fal
=============================================================================

class MyClass
end

function create_MyClass()
  return MyClass()
end

function __main__()
  > "Main was run."
end

=============================================================================
=== tic
=============================================================================

FALCON=/path/to/falcon/bin/falcon

/bin/rm -f test.fal test.fam
for i in 0 1 2 3 4 5 6 7 8 9; do
  cat >test.fal <<EOF
function __main__()
  > "Hello $i."
end
EOF
  echo "Run $i: `${FALCON} test.fal`"
done

=============================================================================

Giancarlo Niccolai

unread,
Apr 27, 2013, 1:16:44 PM4/27/13
to falc...@googlegroups.com
Il 27/04/2013 14:11, garthy...@entropicsoftware.com ha scritto:
> Hi,
>
> I've just started experimenting with embedding Falcon (I'm using
> 0.9.6.8- latest stable), under Debian Linux 6 (64-bit), and I've run
> into a few problems.
>
> Firstly, I'm trying to obtain a class from a loaded file. Using
> "findGlobalSymbol" and "findGlobalItem" doesn't find it, although
> using the main live module and "findModuleItem" does work. However, if
> I get this item, confirm it is callable, and attempt to call it
> ("callItem"), I get an immediate segfault. The docs suggest I should
> be able to call a class (and it presumbly creates an instance of it),
> and "isCallable()" suggests it should be callable, so I'm not quite
> sure what I am doing wrong.
>

The symbol you're searching is not global in the VM. To make it
VM-global, you have to add the "export" keyword to the module you're loaing.

Also, from your code, I see you launch the VM prior doing any of the
operations.

Launching the VM means running the __main__ function of the "main"
module, which is usually the last one loaded in the runtime. After the
launch returns, the VM will probably have Garbage-collected the entities
bound with the module symbols (hence, you find the symbol in the module,
but the associated object is destroyed, and calling or using it results
in a segfalut).

I didn't dig your code, but I suppose that, if you do all the operations
you're doing prior to VMachine::launch(), it should work.

If you see the Falcon.exe code or the Wopi-apache module code (great
source for embedding), you'll see launch() is done after configuring the
VM as desired. Of course, you don't have access to local variables prior
launch(); access to local variables is meant to be working while the
machine is running, from within the machine itself (i.e. from functions
called by your script).

It is also posible to just run specific functions via call(), without
ever invoking launch().

I'll try to help you out if you have further problems.



Garthy

unread,
Apr 27, 2013, 8:39:01 PM4/27/13
to falc...@googlegroups.com

Thankyou for the additional information.

Re locals, I thought it might be something like that- thanks for the clarification. If the problem is detectable, as a suggestion, perhaps it would it be better to raise an exception or error instead?

Adding "export MyClass" caused the findGlobal* calls to work immediately, thanks for that.

I've tried commenting out the "launch" call, so that it is no longer a factor. Unfortunately, tests #1, #3, and #4 still segfault. Ignoring tests #3 and #4 for now, this still leaves test #1 as failing (still at "callItem"). I've also added a test #5 that uses the result from findGlobalItem (once "export MyClass" was added), and it also segfaults at "callItem". If you'd like the updated code, just let me know, but it's easy enough to add in. I think it might not be "launch" at fault in this case.

If I am obtaining the item in the correct way, is there a better way to make the call? If the problem is the way I am obtaining the item, should I be obtaining it in a different way? I'm not quite sure what I'm doing wrong here.

Thanks also for the suggestion re the Wopi-apache code.

Garthy

unread,
Apr 29, 2013, 9:15:02 AM4/29/13
to falc...@googlegroups.com
A quick update:

Whilst experimenting with better ways to create an object of a given class, I may have stumbled onto a workaround by accident. If you declare a variable as part of the class, and it happens to be an array, then test #1 starts to work.

Basically, change:

class MyClass
end

to:

class MyClass
  a = []
end

... and it works.

Using something like "a = 1" isn't enough- you need something along the lines of an array. I don't know why.

The crash case and working case appear to correlate with whether "asClass()->constructor()" for the class returns a nil or not. Basically, the first case above returns a nil, and the second (I think) returns a callable method.

So, at the least, it appears the problem can be detected. In my case, I've got control over the code that is the target of the create-an-object call, or can insist that script writers follow that guideline (and throw an exception if they don't), so I can easily work around it.

As for an actual bugfix? I'm probably too new to Falcon and the API to have much of a chance of tracking down the cause at this point. All I can say is that from what I've noticed, if the class is too "simple", "constructor" returns nil and "callItem" crashes; and if the class is complex "enough", "constructor" returns an item, and "callItem" succeeds. I hope that gives a bit of a hint as to what might really be going wrong.

Paul Davey

unread,
Apr 30, 2013, 10:46:13 AM4/30/13
to falc...@googlegroups.com
Hi Garthy,

Calling the class is not the correct way to create an instance of it in 0.9.  Instead use the createInstance method of CoreClass which you can get an instance of out of the item you retrieve.

Also we are going to be releasing an alpha level preview of the 1.0 engine soon when we finish porting the essential modules and get some documentation together.  You can find this code in the new_engine branch in git.
Also I suggest that if you use the 0.9 version you use the current master git branch as this has some bugfixes and is generally more up to date than 0.9.6.8 that you said you were using (the last official release I believe was 0.9.6.9).

Out of interest what are you embedding falcon for?

Paul.




--
You received this message because you are subscribed to the Google Groups "FalconPL" group.
To unsubscribe from this group and stop receiving emails from it, send an email to falconpl+u...@googlegroups.com.
To post to this group, send email to falc...@googlegroups.com.
Visit this group at http://groups.google.com/group/falconpl?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Garthy

unread,
Apr 30, 2013, 7:39:19 PM4/30/13
to falc...@googlegroups.com

Thankyou for the additional information.

> Calling the class is not the correct way to create an instance of it in 0.9.  Instead use the createInstance method of CoreClass which you can get an instance of out of the item you retrieve.

Is it possible to use createInstance() to create an instance of a class that has arguments? With callItem() I know that you can use pushParameter() to specify all of the arguments for class creation, and specify the number of arguments that you have pushed. createInstance() doesn't include an argument count. I'm not quite sure how to get the arguments in.

Just to note: isCallable() returns true for classes, plus the documentation lists classes as callable objects (in the description for "callItem"- http://falconpl.org/docs/engine/current/d6/d32/classFalcon_1_1VMachine.html#f37c9c1a11fb09ce8d448a4468d3d97d ), plus calling it for non-simple classes (non-simple) works. All indications suggest that it should be possible.

However, if it is not correct, and createInstance() should be used instead, I'm guessing the correct way would be something like this:

Falcon::CoreClass *cls = GetClassSomehow();
vector <Falcon::Item> args = GetArgsSomehow();
...?
Falcon::CoreObject *obj = cls->createInstance();
...?
Falcon::Item iobj(obj);

?

What I'm unsure about is how to get the arguments in and properly create the new instance. I wonder if "constructor()" is applicable, but it returns nil sometimes so can't always be used.


> the last official release I believe was 0.9.6.9

The website presently lists 0.9.6.8 as the official release:

http://www.falconpl.org/index.ftd?page_id=official_download

Seven of the links (including the one I used- the source download)  are for 0.9.6.8. Although now that you mention it, I've noticed that the last link on that page is to a *0.9.6.9* Windows installer.

Paul Davey

unread,
May 10, 2013, 9:34:11 AM5/10/13
to falc...@googlegroups.com
Hi

After speaking with jonnymind a while back I think I have a solution for you, you need to call the VM's reset method before doing any of your calls so that it has a context, this should clear up the segfaults for you.

Also again I suggest looking into the 1.0 engine.

Also I don't think you ever said what you were using falcon for, I am still interested to hear :)

Regards,
Paul


Garthy

unread,
May 10, 2013, 10:11:46 PM5/10/13
to falc...@googlegroups.com
Hi Paul,

Thankyou for the potential solution. I've tried adding vm->reset() in at various places, ranging from immediately after creation, to immediately before the item call, including before and after linking. I'm afraid that test #1 still segfaults, in each place I tried to put this extra call.

I'll keep an eye out for the 1.0 engine. At this stage I am probably best off sticking to the latest official stable release- I'm quite familiar with the risks and rewards of working with a cutting-edge release, and it is not suitable to make the switch right now. I can see myself making the jump later on down the track though.

As for what I'm using Falcon for, it is being used in a presently unreleased C++-based project that I'm not talking about too much at the moment- apologies for being vague. However, from a technical perspective, it is being used to implement plugin scripts in this project.

The ideal language for the task would be a reasonably modern object-oriented language with a well-designed API that has multithreaded use as a key design consideration. I don't need true, complete thread-safe interactions within a single execution context, but I do need multiple execution contexts that can be safely run in their own threads such that they don't interfere with each other.

You'd be surprised how few languages fit those criteria. Falcon meets all of them. I have also found that if you avoid the less-travelled codepaths, Falcon 0.9.6.8 is remarkably stable, even under heavy testing. I did run into the problems outlined at the start of the thread though, and some others, but in each case it has been possible to work around the problem in some way.

Cheers,
Garth
Reply all
Reply to author
Forward
0 new messages