Possible memory leak in python c_code_generation template?

Hi :wave:

I am using python to generate C code for an MPC controller and then use that C code in a C++ environment. Everything works as expected with my MPC. However, due to the unit tests of our high level framework, I have come to the realisation that something strange is happening with the destruction of pointers/capsule in the acados_solver_model files. The problem is the following:

  • I have an object that, when constructed, calls the following functions:
  p_acados_ocp_capsule_ = model_acados_create_capsule();
  int status = model_acados_create(p_acados_ocp_capsule_);
  p_nlp_config_ = model_acados_get_nlp_config(p_acados_ocp_capsule_);
  p_nlp_dims_ = model_acados_get_nlp_dims(p_acados_ocp_capsule_);
  p_nlp_in_ = model_acados_get_nlp_in(p_acados_ocp_capsule_);
  p_nlp_out_ = model_acados_get_nlp_out(p_acados_ocp_capsule_);
  p_nlp_solver_ = model_acados_get_nlp_solver(p_acados_ocp_capsule_);
  p_nlp_opts_ = model_acados_get_nlp_opts(p_acados_ocp_capsule_);
  std::cout << "p_acados_ocp_capsule_" << std::endl;
  std::cout << p_acados_ocp_capsule_ << std::endl;
  std::cout << "p_nlp_config_" << std::endl;
  std::cout << p_nlp_config_ << std::endl;

This is the destructor of that same object (the prints are there for debugging purposes):

  // Free solver
  int status = model_acados_free(p_acados_ocp_capsule_);
  if (status) {
    printf("model_acados_free() returned status %d. \n", status);
  }
  // Free capsule
  status = model_acados_free_capsule(p_acados_ocp_capsule_);
  if (status) {
    printf("model_acados_free_capsule() returned status %d. \n", status);
  }

These pointers are defined in the header file as follows:

  model_solver_capsule *p_acados_ocp_capsule_;
  ocp_nlp_config *p_nlp_config_;
  ocp_nlp_dims *p_nlp_dims_;
  ocp_nlp_in *p_nlp_in_;
  ocp_nlp_out *p_nlp_out_;
  ocp_nlp_solver *p_nlp_solver_;
  void *p_nlp_opts_;

Now, when executing my code normally I can run it without issues, it works as it’s supposed to and everything looks good. However, when doing very simple unit tests, I encounter that some of these unit tests fail. In particular, I noticed that when running the unit tests as a batch run, some fail. When running them individually, though, they run successfully. This has lead me to think that there is some memory leak somewhere in the creation/deletion of the capsule. To investigate further, I run these two unit tests, and check the addresses of the capsules created, and of the nlp_config object created.

TEST(MPC, ConstructorTest) {
  Object_constructor()
}

TEST(MPC, ConstructorTest1) {
 Object_constructor()
}

This is the output:
image

As you see, the pointer p_acados_ocp_capsule_ points to different memory addresses for different constructors, but the pointer p_nlp_config_ points to the same address in both instances, which hints somehow that these two tests are unintendedly sharing information. I have tried to debug why this is the case in the acados_solver_model.c file but I can’t seem to find what’s wrong.

Would appreciate to know if I am doing something wrong in the creation/destruction of the objects.

Best,

Angel.

Hi Angel,

thanks for reporting this!

It would be ideal if you could share a minimal code to reproduce that behavior.

Do you destroy the first solver object and capsule before creating the second one? Then it could in general be that the same memory is used.

What framework are you using to perform batch runs? Sorry I am not familiar with that.

Unfortunately, I couldn’t spot an error yet.

Best,
Jonathan

Hi Jonathan, thanks for the reply!

So, the tests are google tests, and yes, they should be completely independent in all senses, meaning that the object is destroyed when the second constructor is created. So, it might very well be the case, as you say, that they always use the same address for the config object (but not for the capsule object, which makes it a bit suspicious for me).

Unfortunately, I don’t have a minimal code example, since the behavior happens when testing the very big project I am including acados in, but I can describe it. There are two tests (that run one after the other) :

  • Test 1: I construct an MPC object that doesn’t do any task, in one of the independent google tests
  • Test 2: I construct an MPC object that is set up with a reference and I execute the solver to get the first solution. Then I compare this solution with what it should be for sanity.

Test 2 fails when Test 1 is run before. However, if I comment out Test 1, Test 2 succeeds. That is why I believe that some internal configuration or some internal state of the solver is being kept between the two calls. It all hints that some of the objects are not being destroyed and created properly, but I can’t understand why.

I also print now the addresses of all the other pointers:

It really looks a bit suspicious to me that p_nlp_config_ is the only one that keeps the address between two different independent calls to the constructor.

Follow up question: is there a way of clearing/resetting the internal state of the nlp solver? If I put this function in the constructor before I do anything else, then my problem should go away. Thanks!

I think it is probably just a coincidence that the memory for ocp_nlp_config is allocated in the same place twice in a row. Conceptually, the allocation and destruction is done the same way for ocp_nlp_dims, ocp_nlp_in, etc…

You can only set the current iterate of the NLP solver, i.e. initialize it like it is done from Python in those two functions:

Cheers!