The “***Exception: Illegal” error of running googletest

Today, a college told me his project testcases (using googletest) run failed with following errors:

1/6 Test #1: AddTest ....................***Exception: Illegal  1.70 sec
......

I am not familiar with his work nor an expert of googletest, but interested about this exception. After some searching, I bumped into the words from this post:

Given that the GROMACS build system enabled AVX2 SIMD on your VM which seems to not support anything above SSE2, it’s not a surprise that the first math instruction crashes the run.

Immediate solution: set -DGMX_SIMD=SSE2 when configuring.

So my buddy seemed meet the similar problem. After discussing with him, he confirmed his server is also a virtual machine and his issue is the same root cause. To satisfy my own curiosity, I download tfhe whose testcases use AVX and FMA while my machine can only support SSE. Run “make test“:

# make test
Running tests...
Test project /root/Project/tfhe/build
    Start 1: unittests-nayuki-portable
1/4 Test #1: unittests-nayuki-portable ........   Passed    8.59 sec
    Start 2: unittests-nayuki-avx
2/4 Test #2: unittests-nayuki-avx .............***Exception: Illegal  2.88 sec
    Start 3: unittests-spqlios-avx
3/4 Test #3: unittests-spqlios-avx ............***Exception: Illegal  2.86 sec
    Start 4: unittests-spqlios-fma
4/4 Test #4: unittests-spqlios-fma ............***Exception: Illegal  2.85 sec

25% tests passed, 3 tests failed out of 4

Total Test time (real) =  17.19 sec

The following tests FAILED:
          2 - unittests-nayuki-avx (ILLEGAL)
          3 - unittests-spqlios-avx (ILLEGAL)
          4 - unittests-spqlios-fma (ILLEGAL)
Errors while running CTest
make: *** [Makefile:95: test] Error 8

We can see “Exception: Illegal” errors are reported again.

How to harness company’s resource?

As an employee, it is no doubt that we should spare no effort to contribute to your employer since it pays us salary. But at the same time, we should also consider how to utilise the company’s resource to enrich ourselves. After all, only if we become more competent and brilliant, the company can benefit more from us, and this will be a definitely win-win situation. In this post, I will illuminate how to take advantage of company’s “hardware” and “software” resource.

(1) “Hardware resource”: The company has many equipments and devices which the single person can’t afford. During my work in Aicent, we have servers embedded with SPARC processor. X86 processor is ubiquitous whilst SPARC is not so common, so I have a very precious opportunity to learn about this RISC architecture: its instruction set, register window, etc. Another example is in HP/HPE, where I can harness the best servers in this world, this is a really amazing experience! As my manager said, the intranet has all the materials about HP/HPE server, and no one has said you can’t learn it. So whether exploit this treasure or not totally depends on yourself.

(2) “Software resource”: Without working in the same company, you may not recognize your current colleagues, so please cherish this luck. You should always try to “steal” knowledge from your partners. For example, A previous HP/HPE fellow is an expert in Linux, and we has the cooperation in a performance tuning task. During the whole work, I tried my best to learn many skills in profiling and taming Linux from him, and the gain still take effect to date. The other instance is many companies may provide training or online courses. So grab these chances!

Hope everyone can fulfil his own work and improve yourself at the same time! Good luck!

The tips of using and debugging C++ std::iostream

(1) Be cautious of '\n' and std::endl.

The std::endl will flush the output buffer while '\n' not. For example, in socket programming, if client sends message to server using '\n', like this:

out << "Hello World!" << '\n';
out << "I am coming" << '\n';

The server may still block in reading operation and no data is fetched. So you should use std::endl in this case:

out << "Hello World!" << std::endl;
out << "I am coming" << std::endl;

(2) Use std::ios::rdstate.

std::ios::rdstate is a handy function to check the stream state. You can use it in gdb debugging:

(gdb) p in.rdstate()
$45 = std::_S_goodbit
(gdb) n
350             return in;
(gdb) p in.rdstate()
$46 = std::_S_failbit

Through step-mode, we can see which operation crashes the stream.

(3) Serialize the data into file.

No matter you want to do test or debug issue, dump the data into a file and read it back is a very effective method:

std::ofstream ofs("data.txt");
ofs << output;
ofs.close();

std::ifstream ifs("data.txt");
ifs >> input;
ifs.close();

The above simple code can verify whether your serialization functions are correct or not. Trust me, it is a very brilliant trouble-shouting std::iostream issue trick, and it saved me just now!

Parse BPF_ARRAY macro in bcc

BPF_ARRAY is a very common macro used in bcc scripts. To analyze it, I put all BPF_ARRAY related macros in an example file:

// Changes to the macro require changes in BFrontendAction classes
#define BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, _flags) \
struct _name##_table_t { \
  _key_type key; \
  _leaf_type leaf; \
  _leaf_type * (*lookup) (_key_type *); \
  _leaf_type * (*lookup_or_init) (_key_type *, _leaf_type *); \
  int (*update) (_key_type *, _leaf_type *); \
  int (*insert) (_key_type *, _leaf_type *); \
  int (*delete) (_key_type *); \
  void (*call) (void *, int index); \
  void (*increment) (_key_type); \
  int (*get_stackid) (void *, u64); \
  _leaf_type data[_max_entries]; \
  int flags; \
}; \
__attribute__((section("maps/" _table_type))) \
struct _name##_table_t _name = { .flags = (_flags) }

#define BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries) \
BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, 0);

#define BPF_ARRAY1(_name) \
  BPF_TABLE("array", int, u64, _name, 10240)
#define BPF_ARRAY2(_name, _leaf_type) \
  BPF_TABLE("array", int, _leaf_type, _name, 10240)
#define BPF_ARRAY3(_name, _leaf_type, _size) \
  BPF_TABLE("array", int, _leaf_type, _name, _size)

// helper for default-variable macro function
#define BPF_ARRAYX(_1, _2, _3, NAME, ...) NAME

// Define an array function, some arguments optional
// BPF_ARRAY(name, leaf_type=u64, size=10240)
#define BPF_ARRAY(...) \
  BPF_ARRAYX(__VA_ARGS__, BPF_ARRAY3, BPF_ARRAY2, BPF_ARRAY1)(__VA_ARGS__)

enum stat_types {
  S_COUNT = 1,
  S_MAXSTAT
};

void main(void)
{
  BPF_ARRAY(stats, u64, S_MAXSTAT + 1); 
}

Use gcc -E to preprocess it (I have formatted code to make it clear):

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
# 38 "test.c"
enum stat_types {
    S_COUNT = 1,
    S_MAXSTAT
};

void main(void)
{
    struct stats_table_t { 
      int key; 
      u64 leaf; 
      u64 * (*lookup) (int *); 
      u64 * (*lookup_or_init) (int *, u64 *); 
      int (*update) (int *, u64 *); 
      int (*insert) (int *, u64 *); 
      int (*delete) (int *);
      void (*call) (void *, int index); 
      void (*increment) (int); 
      int (*get_stackid) (void *, u64); 
      u64 data[S_MAXSTAT + 1]; 
      int flags; 
    }; 
    __attribute__((section("maps/" "array"))) struct stats_table_t stats = { .flags = (0) };;
}

Let’s analyze how the final result is got:

(1)

BPF_ARRAY(stats, u64, S_MAXSTAT + 1)    

will be expanded to (__VA_ARGS__ will be replaced by all arguments):

BPF_ARRAYX(stats, u64, S_MAXSTAT + 1, BPF_ARRAY3, BPF_ARRAY2, BPF_ARRAY1)(stats, u64, S_MAXSTAT + 1)

(2) According to:

// helper for default-variable macro function
#define BPF_ARRAYX(_1, _2, _3, NAME, ...) NAME

BPF_ARRAYX(stats, u64, S_MAXSTAT + 1, BPF_ARRAY3, BPF_ARRAY2, BPF_ARRAY1)(stats, u64, S_MAXSTAT + 1) will be replaced by following code:

BPF_ARRAY3(stats, u64, S_MAXSTAT + 1)

We can compared it to BPF_ARRAY1 (both _leaf_type and size have default values) and BPF_ARRAY2 (only size have default value.).

(3) Convert BPF_ARRAY3 to final BPF_F_TABLE is more straightforward, so I won’t drill that down.

Use the same method you can analyze other macros, such as BPF_HASH. Hope this small post can give you a tip!