Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] LZ4 decompression error when using r.patch with MASK in effect #2809

Closed
ericrpatton opened this issue Feb 8, 2023 · 58 comments
Closed
Labels
bug Something isn't working C Related code is in C
Milestone

Comments

@ericrpatton
Copy link

ericrpatton commented Feb 8, 2023

Describe the bug
A clear and concise description of what the bug is.
I receive the following error when trying to patch a series of raster maps together when a MASK is in existence:

r.patch in=Leg1_01_20m,Leg1_02_20m,Leg1_03_20m,Leg2_01_20m,Leg4_05_20m out=AAA --v --o

Percent complete...
WARNING: LZ4 decompressionWARNING: LZ4 decompression error
error
ERROR: Error uncompressing null dataERROR: Error uncompressing null data for row 5 of
for row 43 of
WARNING: Unable to close file fcell for raster map AAA: Bad file descriptor
WARNING: Unable to close file nullcmpr for raster map AAA: Bad file
descriptor
double free or corruption (!prev)
Aborted (core dumped)

To Reproduce
Steps to reproduce the behavior:

  1. Create a raster MASK: r.mask raster=filename
  2. set region: g.region rast=MASK res=20 -a
  3. Run r.patch: r.patch in=Map_A,Map_B,Map_C,Map_D out=outputmap --v --o
  4. See error above

Expected behavior
A clear and concise description of what you expected to happen.
No LZ4 decompression errors are encountered, and r.patch competes successfully.

System description (please complete the following information):

uname -a:
Linux machinename 5.15.0-58-generic #64~20.04.1-Ubuntu SMP Fri Jan 6 16:42:31 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

Grass version:

version=8.3.dev
date=2023
revision=2a7d5ec65e
build_date=2023-02-08
build_platform=x86_64-pc-linux-gnu
build_off_t_size=8
libgis_revision=d22ee4748e
libgis_date=2023-02-02T07:48:36+00:00
proj=8.2.0
gdal=3.5.3
geos=3.11.0
sqlite=3.31.1

lz4 --version
*** LZ4 command line interface 64-bits v1.9.2, by Yann Collet ***

python3 -c "import sys, wx; print(sys.version); print(wx.version())"
3.8.10 (default, Nov 14 2022, 12:59:47)
[GCC 9.4.0]
4.0.7 gtk3 (phoenix) wxWidgets 3.0.4

Additional context
Add any other context about the problem here.

@ericrpatton ericrpatton added the bug Something isn't working label Feb 8, 2023
@nilason
Copy link
Contributor

nilason commented Feb 8, 2023

Is this something new, or is it reproducible on any of the latest releases (7.8, 8.0 or 8.2)?

@ericrpatton
Copy link
Author

Is this something new, or is it reproducible on any of the latest releases (7.8, 8.0 or 8.2)?

I'll check it against the 8.2 release and report back in a bit.

@nilason
Copy link
Contributor

nilason commented Feb 8, 2023

Is this something new, or is it reproducible on any of the latest releases (7.8, 8.0 or 8.2)?

I'll check it against the 8.2 release and report back in a bit.

Good!

If you could also share a minimal set of files to reproduce this, I'd be more than thankful.

@ericrpatton
Copy link
Author

ericrpatton commented Feb 8, 2023

Good!

If you could also share a minimal set of files to reproduce this, I'd be more than thankful.

I also get the same error in 8.2.1.

I've uploaded a tar package containing these files (.pack files created with r.pack):
Leg1_01_20m.pack
Leg3_10_datalistp_20m.pack
MASK.pack
mask_region.pack
Grass_Location_proj.txt

The first two files I'm trying to patch together while using the provided MASK which is a reclass map of mask_region. Projection is EPSG:3395 (World Mercator).

Total size is 100MB, uploaded to Proton Drive here:

https://drive.proton.me/urls/NDEPXAKBT8#7dYwTX0iAGs6

@nilason
Copy link
Contributor

nilason commented Feb 8, 2023

Many thanks! We'll see what I can do.

@ericrpatton
Copy link
Author

I'm also getting ZSTD errors when running the same r.patch command with a mask in effect:

WARNING: ZSTD compression error -10: Unknown frame descriptor WARNING: ZSTDWARNING: ZSTD compression error -72: Src size is incorrect ERROR: Error uncompressing raster data for row 376 of <MASK> WARNING: ZSTD compression error -10: Unknown frame descriptor compression error -72: Src size is incorrect Segmentation fault (core dumped)

r.patch completes normally with no mask though.

@nilason
Copy link
Contributor

nilason commented Feb 8, 2023

For some reason I haven't got the MASK.pack file to work (problem with referencing mask_region in another non-existing location). I did test with creating mask directly with mask_region though and patched without issues. I'll do some more testing, but thought letting you know.

(FWIW, I'm testing on recent 8.3.dev on Mac).

@ericrpatton
Copy link
Author

For some reason I haven't got the MASK.pack file to work (problem with referencing mask_region in another non-existing location). I did test with creating mask directly with mask_region though and patched without issues. I'll do some more testing, but thought letting you know.

(FWIW, I'm testing on recent 8.3.dev on Mac).

OK, thanks for testing. I will post gdb backtrace output soon, maybe that will shed some light.

@nilason
Copy link
Contributor

nilason commented Feb 8, 2023

Just a thought, is the reclassified mask_region (your MASK) valid as mask file (as per https://grass.osgeo.org/grass82/manuals/r.mask.html)? I cannot check now myself.

@nilason
Copy link
Contributor

nilason commented Feb 9, 2023

I now solved my initial problem with non-existing mapset. My tests succeeded, I couldn't reproduce your issues:

GRASS : grass > r.patch in=Leg1_01_20m,Leg3_10_datalistp_20m out=outputmap --v --o
Percent complete...
 100%
Creating support files for raster map <outputmap>...
[Raster MASK present]                                                                  
GRASS : grass > export GRASS_COMPRESSOR=LZ4            Mapset <UNB_2016> in <test_lz4>
[Raster MASK present]                                                                  
GRASS : grass > r.patch in=Leg1_01_20m,Leg3_10_datalistp_20m out=outputmap --v --o
Percent complete...
 100%
Creating support files for raster map <outputmap>...
[Raster MASK present]                                                                  
GRASS : grass > r.compress outputmap -p                Mapset <UNB_2016> in <test_lz4>
<outputmap> is compressed (method 3: LZ4). Data type: FCELL
<outputmap> has a compressed NULL file
[Raster MASK present]                                                                  
GRASS : grass >                                        Mapset <UNB_2016> in <test_lz4>

@ericrpatton
Copy link
Author

Just a thought, is the reclassified mask_region (your MASK) valid as mask file (as per https://grass.osgeo.org/grass82/manuals/r.mask.html)? I cannot check now myself.

Yes, it was created with 'r.mask raster=mask_region'.

@ericrpatton
Copy link
Author

I now solved my initial problem with non-existing mapset. My tests succeeded, I couldn't reproduce your issues:

GRASS : grass > r.patch in=Leg1_01_20m,Leg3_10_datalistp_20m out=outputmap --v --o
Percent complete...
 100%
Creating support files for raster map <outputmap>...
[Raster MASK present]                                                                  
GRASS : grass > export GRASS_COMPRESSOR=LZ4            Mapset <UNB_2016> in <test_lz4>
[Raster MASK present]                                                                  
GRASS : grass > r.patch in=Leg1_01_20m,Leg3_10_datalistp_20m out=outputmap --v --o
Percent complete...
 100%
Creating support files for raster map <outputmap>...
[Raster MASK present]                                                                  
GRASS : grass > r.compress outputmap -p                Mapset <UNB_2016> in <test_lz4>
<outputmap> is compressed (method 3: LZ4). Data type: FCELL
<outputmap> has a compressed NULL file
[Raster MASK present]                                                                  
GRASS : grass >                                        Mapset <UNB_2016> in <test_lz4>

It seems I run into the LZ4 decompression error whenever I try doing a raster operation (r.patch, r.resamp.interp, etc.) when a mask is created in a very large region. After some more investigation, I found I was able to use r.patch and r.resamp.interp successfully with a mask enabled in relatively small region. When doing a r.patch operation while using a mask in a region with 1,017,578,100 cells in it causes the aforementioned lz4 error.

@nilason
Copy link
Contributor

nilason commented Feb 9, 2023

I increased the resolution in the same location I previously worked in to 1,098,572,200 cells. Still couldn't reproduce the error. Please share a backtrace log if you can.

@ericrpatton
Copy link
Author

Ok, here's the backtrace:

gdb r.patch
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
http://www.gnu.org/software/gdb/bugs/.
Find the GDB manual and other documentation resources online at:
http://www.gnu.org/software/gdb/documentation/.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from r.patch...
(No debugging symbols found in r.patch)

Starting program: /usr/local/grass83/bin/r.patch
in=Leg1_01_resamp_100m_NL_Bioregion,Leg1_02_resamp_100m_NL_Bioregion,Leg1_03_resamp_100m_NL_Bioregion,Leg1_04_resamp_100m_NL_Bioregion,Leg2_01_resamp_100m_NL_Bioregion,Leg4_01_resamp_100m_NL_Bioregion
output=TEST nprocs=16 memory=10000 --overwrite
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffe0bdba700 (LWP 902712)]
[New Thread 0x7ffe0b5b9700 (LWP 902713)]
[New Thread 0x7ffe0adb8700 (LWP 902714)]
[New Thread 0x7ffe0a5b7700 (LWP 902715)]
[New Thread 0x7ffe09db6700 (LWP 902716)]
[New Thread 0x7ffe095b5700 (LWP 902717)]
[New Thread 0x7ffe08db4700 (LWP 902718)]
[New Thread 0x7ffe085b3700 (LWP 902719)]
[New Thread 0x7ffe07db2700 (LWP 902720)]
[New Thread 0x7ffe075b1700 (LWP 902721)]
[New Thread 0x7ffe06db0700 (LWP 902722)]
[New Thread 0x7ffe065af700 (LWP 902723)]
[New Thread 0x7ffe05dae700 (LWP 902724)]
[New Thread 0x7ffe055ad700 (LWP 902725)]
[New Thread 0x7ffe04dac700 (LWP 902726)]
WARNING: ZSTD compression error -72: Src size is incorrect
ERROR: Error uncompressing raster data for row 118000 of
WARNING: WARNING: LZ4 decompression error
LZ4 decompression error
WARNING: ZSTD compression error -72: Src size is incorrect

Thread 5 "r.patch" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffe0a5b7700 (LWP 902715)]
0x00007ffff7cf1f80 in __cxa_finalize (d=0x7ffff61d4000) at cxa_finalize.c:40
40 cxa_finalize.c: No such file or directory.

(gdb) bt
#0 0x00007ffff7cf1f80 in __cxa_finalize (d=0x7ffff61d4000) at cxa_finalize.c:40
#1 0x00007ffff5eea847 in ?? () from /lib/x86_64-linux-gnu/libproj.so.22
#2 0x00007ffe0a5b64d0 in ?? ()
#3 0x00007ffff7fe0f6b in _dl_fini () at dl-fini.c:138
Backtrace stopped: frame did not save the PC

@nilason
Copy link
Contributor

nilason commented Feb 9, 2023

Thanks! Have to admit, I didn't get much wiser with that though. The trace path through GRASS is missing.

Perhaps g.gisenv set="DEBUG=3" could give som hints.

@nilason
Copy link
Contributor

nilason commented Feb 9, 2023

and zstd --version?

@ericrpatton
Copy link
Author

Thanks! Have to admit, I didn't get much wiser with that though. The trace path through GRASS is missing.

Perhaps g.gisenv set="DEBUG=3" could give som hints.

Do you want the command run inside gdb with the DEBUG=3 or outside it?

@ericrpatton
Copy link
Author

ericrpatton commented Feb 9, 2023

and zstd --version?

I didn't actually have the binary zstd installed as it turns out, but I had libzstd1 and libzstd-dev already installed. Grass configure never complained when it was run.

As of now, all zstd libraries and binary are version 1.4.4.

@nilason
Copy link
Contributor

nilason commented Feb 9, 2023

Thanks! Have to admit, I didn't get much wiser with that though. The trace path through GRASS is missing.
Perhaps g.gisenv set="DEBUG=3" could give som hints.

Do you want the command run inside gdb with the DEBUG=3 or outside it?

I was thinking of just an ordinary run of the r.patch after setting DEBUG.

Could be that the gdb debugging reveals little about GRASS because it wasn't build with -g flag.

@ericrpatton
Copy link
Author

I was thinking of just an ordinary run of the r.patch after setting DEBUG.

Ok, I set DEBUG=3, re-ran it and attached the output as a text file; ~24K lines long, though!

r.patch_debug.txt

@nilason
Copy link
Contributor

nilason commented Feb 9, 2023

Thanks! Getting a better picture of it, but nowhere close to a solution. Sooo difficult without being able to reproduce it.

Could you perhaps try with nprocs=1, just a long shot but...

@ericrpatton
Copy link
Author

Thanks! Getting a better picture of it, but nowhere close to a solution. Sooo difficult without being able to reproduce it.

Could you perhaps try with nprocs=1, just a long shot but...

Thats it! With a MASK in effect, r.patch completes with nprocs=1, but fails with any value > 1. If I remove the MASK, r.patch completes with any value of nprocs.

@nilason
Copy link
Contributor

nilason commented Feb 10, 2023

Grand! At last we narrowed it down a bit. Still, I did try with nprocs>1, but couldn't reproduce the failure. That's why I figured it a long shot, but I guess this is the "problem" with parallel code: frustratingly difficult to debug.

Just to rule out, would adding -z to r.patch with nprocs>1 make any difference?

Could you reproduce the failure also with the test files you made available? (Creating new location, r.unpack, etc...)

@ericrpatton
Copy link
Author

Just to rule out, would adding -z to r.patch with nprocs>1 make any difference?

It makes no difference, unfortunately. Similar errors as before.

Could you reproduce the failure also with the test files you made available? (Creating new location, r.unpack, etc...)

Wow, I unpacked my files into a new location, and Grass can't read the header files of the rasters. You didn't have any problem reading them?

@nilason
Copy link
Contributor

nilason commented Feb 10, 2023

Could you reproduce the failure also with the test files you made available? (Creating new location, r.unpack, etc...)

Wow, I unpacked my files into a new location, and Grass can't read the header files of the rasters. You didn't have any problem reading them?

I had to create a mapset "UNB_2016" and activate and unpack into that.

@neteler
Copy link
Member

neteler commented Feb 10, 2023

Could be that the gdb debugging reveals little about GRASS because it wasn't build with -g flag.

@ericrpatton did you recompile (after make distclean) GRASS with -g? Then gdb will be more chatty.

@ericrpatton
Copy link
Author

Could be that the gdb debugging reveals little about GRASS because it wasn't build with -g flag.

@ericrpatton did you recompile (after make distclean) GRASS with -g? Then gdb will be more chatty.

Hi @neteler , I ran make distclean, and recompiled with CFLAGS="-ggdb -Wall -Werror-implicit-function-declaration" as given on the Grass Wiki. Here is the gdb output:

`gdb r.patch
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
http://www.gnu.org/software/gdb/bugs/.
Find the GDB manual and other documentation resources online at:
http://www.gnu.org/software/gdb/documentation/.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from r.patch...
(gdb) run in=Leg1_01_20m,Leg2_01_20m,Leg2_02_20m,Leg3_10_datalistp_20m out=TEST_nprocs_16 nprocs=16 mem=10000 --v
Starting program: /usr/local/grass83/bin/r.patch in=Leg1_01_20m,Leg2_01_20m,Leg2_02_20m,Leg3_10_datalistp_20m out=TEST_nprocs_16 nprocs=16 mem=10000 --v
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Percent complete...
[New Thread 0x7ffefe5e7700 (LWP 71859)]
[New Thread 0x7ffefdde6700 (LWP 71860)]
[New Thread 0x7ffefd5e5700 (LWP 71861)]
[New Thread 0x7ffefcde4700 (LWP 71862)]
[New Thread 0x7ffefc5e3700 (LWP 71863)]
[New Thread 0x7ffefbde2700 (LWP 71864)]
[New Thread 0x7ffefb5e1700 (LWP 71865)]
[New Thread 0x7ffefade0700 (LWP 71866)]
[New Thread 0x7ffefa5df700 (LWP 71867)]
[New Thread 0x7ffef9dde700 (LWP 71868)]
[New Thread 0x7ffef95dd700 (LWP 71869)]
[New Thread 0x7ffef8ddc700 (LWP 71870)]
[New Thread 0x7ffef85db700 (LWP 71871)]
[New Thread 0x7ffef7dda700 (LWP 71872)]
[New Thread 0x7ffef75d9700 (LWP 71873)]
WARNING: ZSTD compression error -72: Src size is incorrect
WARNING: ZSTD compression error -10: Unknown frame descriptor
ERROR: Error uncompressing raster data for row 9440 of
[Thread 0x7ffef75d9700 (LWP 71873) exited]
[Thread 0x7ffef7dda700 (LWP 71872) exited]
[Thread 0x7ffef85db700 (LWP 71871) exited]
[Thread 0x7ffef8ddc700 (LWP 71870) exited]
[Thread 0x7ffef95dd700 (LWP 71869) exited]
[Thread 0x7ffef9dde700 (LWP 71868) exited]
[Thread 0x7ffefa5df700 (LWP 71867) exited]
[Thread 0x7ffefade0700 (LWP 71866) exited]
[Thread 0x7ffefb5e1700 (LWP 71865) exited]
[Thread 0x7ffefbde2700 (LWP 71864) exited]
[Thread 0x7ffefc5e3700 (LWP 71863) exited]
[Thread 0x7ffefd5e5700 (LWP 71861) exited]
[Thread 0x7ffefdde6700 (LWP 71860) exited]
[Thread 0x7ffefe5e7700 (LWP 71859) exited]
[Thread 0x7ffff16b2bc0 (LWP 71852) exited]
[Inferior 1 (process 71852) exited with code 01]
(gdb) bt
No stack.
(gdb) bt full
No stack.`

No sure why there's not more output :/

@nilason
Copy link
Contributor

nilason commented Feb 10, 2023

No sure why there's not more output :/

I was afraid of this. My guess it's because of multithreading processing.

Did you manage to also test with the sample files?

@ericrpatton
Copy link
Author

ericrpatton commented Feb 10, 2023

No sure why there's not more output :/

I was afraid of this. My guess it's because of multithreading processing.

Did you manage to also test with the sample files?

Yes, the test files seemed to generate (possibly) more informative output in gdb:

> gdb r.patch
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from r.patch...
(gdb) run in=Leg1_01_20m,Leg3_10_datalistp_20m out=test_patch_nprocs_16 nprocs=16 mem=2000 --v --o
Starting program: /usr/local/grass83/bin/r.patch in=Leg1_01_20m,Leg3_10_datalistp_20m out=test_patch_nprocs_16 nprocs=16 mem=2000 --v --o
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Percent complete...
[New Thread 0x7fff839d9700 (LWP 91463)]
[New Thread 0x7fff831d8700 (LWP 91464)]
[New Thread 0x7fff829d7700 (LWP 91465)]
[New Thread 0x7fff821d6700 (LWP 91466)]
[New Thread 0x7fff819d5700 (LWP 91467)]
[New Thread 0x7fff811d4700 (LWP 91468)]
[New Thread 0x7fff809d3700 (LWP 91469)]
[New Thread 0x7fff801d2700 (LWP 91470)]
[New Thread 0x7fff7f9d1700 (LWP 91471)]
[New Thread 0x7fff7f1d0700 (LWP 91472)]
[New Thread 0x7fff7e9cf700 (LWP 91473)]
[New Thread 0x7fff7e1ce700 (LWP 91474)]
[New Thread 0x7fff7d9cd700 (LWP 91475)]
[New Thread 0x7fff7d1cc700 (LWP 91476)]
[New Thread 0x7fff7c9cb700 (LWP 91477)]
WARNING: ZSTD compression error -10: WARNING: WARNING: ZSTD compression error -10: Unknown frame descriptor
ZSTD Unknown frame descriptor
ERROR: Error uncompressing raster data for row 5564 of <MASK>
compression error -72: Src size is incorrect
WARNING: ZSTD compression error -72: Src size is incorrect
corrupted double-linked list

Thread 8 "r.patch" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fff809d3700 (LWP 91469)]
0x00007ffff7cf1876 in __run_exit_handlers (status=1, listp=0x7ffff7e97718 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:77
77	exit.c: No such file or directory.
(gdb) bt full
#0  0x00007ffff7cf1876 in __run_exit_handlers (status=1, listp=0x7ffff7e97718 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:77
        f = 0x10555452892420
        new_exitfn_called = 646
        cur = 0x55555559a830
#1  0x00007ffff7cf1a60 in __GI_exit (status=<optimized out>) at exit.c:139
No locals.
#2  0x00007ffff7f23a8b in G_fatal_error (msg=0x7ffff7fbc5d8 "Error uncompressing raster data for row %d of <%s>") at error.c:165
        busy = 1
        ap = {{gp_offset = 0, fp_offset = 5, overflow_arg_area = 0x7fff809d2570, reg_save_area = 0x7fff00000006}}
#3  0x00007ffff7fa790a in read_data_compressed (fd=1, row=9737, data_buf=0x5555557a1db0 '\001' <repeats 200 times>..., nbytes=0x5555557ac380) at get_row.c:174
        fcb = 0x5555557ac220
        t1 = 806453
        t2 = 806577
        readamount = 123
        bufsize = 20613
        cmp = 0x7fff68019ee1 "(\265/\375`\205O\215\001"
        cmp2 = 0x7fff68019ee0 "\001(\265/\375`\205O\215\001"
        n = -1
#4  0x00007ffff7fa7d02 in read_data (fd=1, row=9737, data_buf=0x5555557a1db0 '\001' <repeats 200 times>..., nbytes=0x5555557ac380) at get_row.c:254
        fcb = 0x5555557ac220
#5  0x00007ffff7fa8c72 in get_map_row_nomask (fd=1, rast=0x7fff68005cc0, row=9737, data_type=0) at get_row.c:596
        transfer_to_cell_FtypeOtype = {{0x7ffff7fa85a5 <transfer_to_cell_XX>, 0x7ffff7fa88c3 <transfer_to_cell_if>, 0x7ffff7fa8a17 <transfer_to_cell_id>}, {0x7ffff7fa86ae <transfer_to_cell_fi>, 0x7ffff7fa85a5 <transfer_to_cell_XX>, 
            0x7ffff7fa8ac0 <transfer_to_cell_fd>}, {0x7ffff7fa87b8 <transfer_to_cell_di>, 0x7ffff7fa896c <transfer_to_cell_df>, 0x7ffff7fa85a5 <transfer_to_cell_XX>}}
        fcb = 0x5555557ac220
        r = 9737
        row_status = 1
#6  0x00007ffff7fa9914 in embed_mask (flags=0x7fff68000c00 '\001' <repeats 200 times>..., row=9737) at get_row.c:1016
        mask_buf = 0x7fff68005cc0
        i = 0
#7  0x00007ffff7fa9aa9 in get_null_value_row (fd=8, flags=0x7fff68000c00 '\001' <repeats 200 times>..., row=9737, with_mask=1) at get_row.c:1045
        fcb = 0x5555557ad3a0
#8  0x00007ffff7fa9b6c in embed_nulls (fd=8, buf=0x55555aae0b50, row=9737, map_type=1, null_is_zero=0, with_mask=1) at get_row.c:1064
        fcb = 0x5555557ad3a0
        size = 4
        null_buf = 0x7fff68000c00 '\001' <repeats 200 times>...
        i = 0
#9  0x00007ffff7fa8d23 in get_map_row_no_reclass (fd=8, rast=0x55555aae0b50, row=9737, data_type=1, null_is_zero=0, with_mask=1) at get_row.c:609
No locals.
#10 0x00007ffff7fa8e01 in get_map_row (fd=8, rast=0x55555aae0b50, row=9737, data_type=1, null_is_zero=0, with_mask=1) at get_row.c:632
        fcb = 0x5555557ad3a0
        size = 4
        temp_buf = 0x0
        buf = 0x55555aae0b50
        type = 1
        i = -2137183984
#11 0x00007ffff7fa8fa3 in Rast_get_row (fd=8, buf=0x55555aae0b50, row=9737, data_type=1) at get_row.c:776
No locals.
#12 0x00005555555578e7 in main._omp_fn.0 () at main.c:220
        north_edge = 0
        south_edge = 0
        p = 0x0
        row = 9737
        t_id = 7
        local_presult = 0x55555aae0b50
        local_patch = 0x55555ac22d50
        local_infd = 0x5555555a13f0
        infd = 0x55555558c020
        statf = 0x5555555a1510
--Type <RET> for more, q to quit, c to continue without paging-- 
        out_type = 1
        out_cell_size = 4
        presult = 0x5555557b4910
        patch = 0x5555557b49a0
        outbuf = 0x7fff839da010
        nfiles = 2
        nrows = 22253
        ncols = 20613
        use_zero = 0
        no_support = 0
        window = {format = 0, compressed = -1, rows = 22253, rows3 = 22253, cols = 20613, cols3 = 20613, depths = 1, proj = 99, zone = 0, ew_res = 20, ew_res3 = 20, ns_res = 20, ns_res3 = 20, tb_res = 1, north = 6719680, 
          south = 6274620, east = -6296000, west = -6708260, top = 1, bottom = 0}
        cellhd = 0x55555558c0b0
        computed = 87
        start = 0
        end = 22253
        i = 0
#13 0x00007ffff7eb778e in ?? () from /lib/x86_64-linux-gnu/libgomp.so.1
No symbol table info available.
#14 0x00007ffff61e3609 in start_thread (arg=<optimized out>) at pthread_create.c:477
        ret = <optimized out>
        pd = <optimized out>
        unwind_buf = {cancel_jmp_buf = {{jmp_buf = {140735351174912, -4943711659669734923, 140737488342414, 140737488342415, 140737352951584, 140735351171904, 4943660784269769205, 4943690479365503477}, mask_was_saved = 0}}, priv = {
            pad = {0x0, 0x0, 0x0, 0x0}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}}
        not_first_call = 0
#15 0x00007ffff7dca133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
No locals.
(gdb) l
72	in exit.c
(gdb) frame 2
#2  0x00007ffff7f23a8b in G_fatal_error (msg=0x7ffff7fbc5d8 "Error uncompressing raster data for row %d of <%s>") at error.c:165
165	        exit(EXIT_FAILURE);
(gdb) l
160	{
161	    static int busy;
162	    va_list ap;
163	
164	    if (busy)
165	        exit(EXIT_FAILURE);
166	    busy = 1;
167	
168	    if (G_verbose() > -1) {
169	        va_start(ap, msg);
(gdb)

@nilason
Copy link
Contributor

nilason commented Feb 13, 2023

I didn't have to, but I believe g.region raster=mask_region should do.

@nilason
Copy link
Contributor

nilason commented Feb 13, 2023

And it should only take a sec or two.

@neteler
Copy link
Member

neteler commented Feb 13, 2023

I believe setting g.region is missing there. I am trying to run it with valgrind now, but it's taking forever.

Right, before r.patch..., add in the script:

g.region raster=Leg1_01_20m,Leg3_10_datalistp_20m -p

(maybe edit the script above in #2809 (comment) ?)

@petrasovaa
Copy link
Contributor

g.region raster=Leg1_01_20m,Leg3_10_datalistp_20m -p

Hm, it's over 13 billion cells, no? So it should take a while (especially with valgrind).

@neteler
Copy link
Member

neteler commented Feb 13, 2023

Ok, with the g.region on all input raster maps I also get the error (tested with nprocs=8):

Note that I hit "enter" frequently in the terminal to see when it would fail.

grassdev --tmp-location EPSG:3395 --exec ./test_2809.sh
Mapset switched.
WARNING: Overriding projection check (using current location's projection).
Raster map <Leg1_01_20m> unpacked
WARNING: Overriding projection check (using current location's projection).
Raster map <Leg3_10_datalistp_20m> unpacked
WARNING: Overriding projection check (using current location's projection).
Raster map <mask_region> unpacked
WARNING: Overriding projection check (using current location's projection).
Raster map <MASK> unpacked
projection: 99 (WGS 84 / World Mercator)
zone:       0
datum:      wgs84
ellipsoid:  wgs84
north:      10559680
south:      6423640
west:       -7203580
east:       -5871920
nsres:      20
ewres:      20
rows:       206802
cols:       66583
cells:      13769497566
Percent complete...
   3%
  12%
  33%
  69%
  72%
  75%
  78%

  81%
  84%

  87%
  90%
WARNING: ZSTD compression error -72: Src size is incorrect
ERROR: Error uncompressing raster data for row 5250 of <MASK>

So, it roughly happened at 90 % of the r.patch job.

@neteler
Copy link
Member

neteler commented Feb 14, 2023

I tried debugging with gdb, using "Attaching to child process" (https://grasswiki.osgeo.org/wiki/GRASS_Debugging#Attaching_to_child_process):

gdb ...
Attaching to process 1423674
[New LWP 1423678]
[New LWP 1423679]
[New LWP 1423680]
[New LWP 1423681]
[New LWP 1423682]
[New LWP 1423683]
[New LWP 1423684]

This GDB supports auto-downloading debuginfo from the following URLs:
https://debuginfod.fedoraproject.org/ 
Enable debuginfod for this session? (y or [n]) y
Debuginfod has been enabled.
To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x00007fb9b7d6b045 in embed_nulls (fd=0, buf=0x369b270, row=6056, map_type=1, null_is_zero=0, with_mask=1) at get_row.c:1069
1069	        if (null_buf[i] || Rast_is_null_value(buf, map_type)) {
(gdb) continue
Continuing.
WARNING: LZ4 decompression error
WARNING: LZ4 decompression error
ERROR: Error uncompressing null data for row 3000 of <MASK>
WARNING: LZ4 decompression error
WARNING: LZ4 decompression error
WARNING: LZ4 free(): corrupted unsorted chunks
decompression error

Thread 2 "r.patch" received signal SIGABRT, Aborted.
[Switching to Thread 0x7fb939673b40 (LWP 1423678)]
__pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
Downloading 0.00 MB source file /usr/src/debug/glibc-2.36-9.fc37.x86_64/nptl/pthread_kill.c
44	      return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0;

(gdb) bt
#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
#1  0x00007fb9b7963ec3 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
#2  0x00007fb9b7913a76 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#3  0x00007fb9b78fd7fc in __GI_abort () at abort.c:79
#4  0x00007fb9b795808e in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fb9b7a71465 "%s\n") at ../sysdeps/posix/libc_fatal.c:155
#5  0x00007fb9b796db9c in malloc_printerr (str=str@entry=0x7fb9b7a74130 "free(): corrupted unsorted chunks") at malloc.c:5660
#6  0x00007fb9b796fd4c in _int_free (av=0x7fb9b7aaac80 <main_arena>, p=0x692150, have_lock=<optimized out>) at malloc.c:4626
#7  0x00007fb9b7972363 in __GI___libc_free (mem=<optimized out>) at malloc.c:3385
#8  0x00007fb9b3f3177b in H5FL__reg_gc_list (head=0x7fb9b41fce80 <H5_H5T_shared_t_reg_free_list>) at ../../src/H5FL.c:529
#9  0x00007fb9b3f325e5 in H5FL_reg_free (head=<optimized out>, obj=<optimized out>) at ../../src/H5FL.c:379
#10 0x00007fb9b40575e4 in H5T_close_real (dt=0x6920f0) at ../../src/H5T.c:4107
#11 0x00007fb9b404bc43 in H5T_top_term_package () at ../../src/H5T.c:1592
#12 0x00007fb9b3e660a0 in H5_term_library () at ../../src/H5.c:339
#13 0x00007fb9b7915b97 in __cxa_finalize (d=0x7fb9b41ea460) at cxa_finalize.c:83
#14 0x00007fb9b3e599a7 in __do_global_dtors_aux () from /lib64/libhdf5.so.200
#15 0x00007fb9396707c0 in ?? ()
#16 0x00007fb9b7d8ea9e in _dl_fini () at dl-fini.c:142
Backtrace stopped: frame did not save the PC

(gdb) bt full
#0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
        tid = <optimized out>
        ret = 0
        pd = <optimized out>
        old_mask = {__val = {0}}
        ret = <optimized out>
#1  0x00007fb9b7963ec3 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
No locals.
#2  0x00007fb9b7913a76 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
        ret = <optimized out>
#3  0x00007fb9b78fd7fc in __GI_abort () at abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0x20, sa_sigaction = 0x20}, sa_mask = {__val = {0, 140435628998081, 0, 0, 0, 0, 0, 0, 0, 0, 140433508729024, 0, 0, 0, 
              206158430224, 140433508729776}}, sa_flags = 963052144, sa_restorer = 0x0}
#4  0x00007fb9b795808e in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fb9b7a71465 "%s\n") at ../sysdeps/posix/libc_fatal.c:155
        ap = {{gp_offset = 24, fp_offset = 4294967295, overflow_arg_area = 0x7fb93966fdb0, reg_save_area = 0x7fb93966fd40}}
        fd = <optimized out>
        list = <optimized out>
        nlist = <optimized out>
        cp = <optimized out>
#5  0x00007fb9b796db9c in malloc_printerr (str=str@entry=0x7fb9b7a74130 "free(): corrupted unsorted chunks") at malloc.c:5660
No locals.
#6  0x00007fb9b796fd4c in _int_free (av=0x7fb9b7aaac80 <main_arena>, p=0x692150, have_lock=<optimized out>) at malloc.c:4626
        size = <optimized out>
        fb = <optimized out>
        nextchunk = 0x6922a0
        nextsize = 112
        nextinuse = <optimized out>
        prevsize = <optimized out>
        bck = 0x7fb9b7aaace0 <main_arena+96>
        fwd = <optimized out>
        __PRETTY_FUNCTION__ = "_int_free"
#7  0x00007fb9b7972363 in __GI___libc_free (mem=<optimized out>) at malloc.c:3385
        ar_ptr = <optimized out>
        p = <optimized out>
        err = 2
#8  0x00007fb9b3f3177b in H5FL__reg_gc_list (head=0x7fb9b41fce80 <H5_H5T_shared_t_reg_free_list>) at ../../src/H5FL.c:529
        tmp = 0x692320
        free_list = <optimized out>
#9  0x00007fb9b3f325e5 in H5FL_reg_free (head=<optimized out>, obj=<optimized out>) at ../../src/H5FL.c:379
        err_occurred = false
        ret_value = 0x0
        __func__ = "H5FL_reg_free"
#10 0x00007fb9b40575e4 in H5T_close_real (dt=0x6920f0) at ../../src/H5T.c:4107
        err_occurred = false
        ret_value = 0
        __func__ = "H5T_close_real"
#11 0x00007fb9b404bc43 in H5T_top_term_package () at ../../src/H5T.c:1592
        path = 0x692470
        i = 98
        nprint = 0
        n = 0
#12 0x00007fb9b3e660a0 in H5_term_library () at ../../src/H5.c:339
        pending = <optimized out>
        ntries = <optimized out>
        n = <optimized out>
        at = 1
        loop = "L\000\033\266\271\177\000\000@oX\265\271\177\000\000M\002\000\000\000\000\000\000\200by\262\271\177\000\000\b\372۷\271\177\000\000\250\005g9\271\177\000\000@oX\265\271\177\000\000\242\245ٷ\271\177\000\000\005", '\000' <repeats 16 times>, "ˎ\267\271\177\000\000\340Z\221\267\271\177\000\000\320\003g9\271\177\000\000\260\003g9\271\177\000\000\320\003g9\271\177\000\000\000\000\000\000\000\000\000\000\b\372۷\271\177\000\000n\311ٷ\271\177\000\000\230\376x\262\271\177\000\000\270\032\253\267\271\177\000\000\001\000\000\000\000\000\000\000\230\376x\262\271\177\000\000\240\376x\262\271\177\000\000\360\200X\265\271\177\000\000\000\000\000\000\000\000\000\000\060Ɖ\267\271\177"...
        func = 0x7fb9b3ef3280 <H5Eprint2>
#13 0x00007fb9b7915b97 in __cxa_finalize (d=0x7fb9b41ea460) at cxa_finalize.c:83
        check = 807
        cxafn = 0x7fb9b3e67220 <H5_term_library>
        cxaarg = <optimized out>
        f = 0x654010
        funcs = 0x653d00
        restart = <optimized out>
#14 0x00007fb9b3e599a7 in __do_global_dtors_aux () from /lib64/libhdf5.so.200
No symbol table info available.
#15 0x00007fb9396707c0 in ?? ()
No symbol table info available.
#16 0x00007fb9b7d8ea9e in _dl_fini () at dl-fini.c:142
        array = 0x0
        i = <optimized out>
        l = 0x7fb9b77e2f30
        maps = 0x7fb9396703e0
        i = 59
        l = <optimized out>
        nmaps = <optimized out>
        nloaded = <optimized out>
        ns = 0
        do_audit = <optimized out>
        __PRETTY_FUNCTION__ = "_dl_fini"
Backtrace stopped: frame did not save the PC
(gdb)

@nilason
Copy link
Contributor

nilason commented Feb 14, 2023

Right, before r.patch..., add in the script:

g.region raster=Leg1_01_20m,Leg3_10_datalistp_20m -p

Now, setting the region to that, I also can reproduce this.
Not with g.region raster=mask_region.

@nilason nilason added the C Related code is in C label Feb 14, 2023
@ericrpatton
Copy link
Author

Glad that at the very least the error is reproducible; thanks for confirming, guys.

@petrasovaa
Copy link
Contributor

I can confirm this too, the openmp implementation did not consider using mask. Reading the rasters themselves works fine, but not with the mask. So a quick solution for 8.3 is to disable parallelization when mask is present. Alternatively the reading could be done in serial, but that would slow it down, or the mask could be disabled for reading and applied at the end. Unfortunately, I suspect this could be a problem for other parallelized modules as well.

@nilason
Copy link
Contributor

nilason commented Feb 14, 2023

I couldn't produce any meaningful backtrace output. But just wanted to share how a debug session could look like with lldb. I ran the r.patch command and put a breakpoint at lib/raster/get_row.c:L174:

GRASS : grass > lldb r.patch -- in=Leg1_01_20m,Leg3_10_datalistp_20m out=outputmap nprocs=8 mem=2000 --v --o                                                                                                Mapset <UNB_2016> in <test_lz4>
(lldb) target create "r.patch"
Current executable set to 'r.patch' (arm64).
(lldb) settings set -- target.run-args  "in=Leg1_01_20m,Leg3_10_datalistp_20m" "out=outputmap" "nprocs=8" "mem=2000" "--v" "--o"
(lldb) breakpoint set --file /Dev/grass/lib/raster/get_row.c --line 174
Breakpoint 1: where = libgrass_raster.8.3.dylib`read_data_compressed + 756 at get_row.c:174:17, address = 0x00000000000175a4
(lldb) run
Process 54428 launched: '/Dev/grass/dist.aarch64-apple-darwin21.6.0/bin/r.patch' (arm64)
Percent complete...
WARNING: ZSTD compression error -10: Unknown frame descriptor
Process 54428 stopped
* thread #8, stop reason = breakpoint 1.1
    frame #0: 0x00000001002275a4 libgrass_raster.8.3.dylib`read_data_compressed(fd=1, row=197250, data_buf="", nbytes=0x000000010400b1e0) at get_row.c:174:17
   171 	            if ((n = G_expand(cmp, readamount, data_buf, bufsize,
   172 	                              fcb->cellhd.compressed)) < 0 ||
   173 	                (unsigned int)n != bufsize) {
-> 174 	                G_fatal_error(
   175 	                    _("Error uncompressing raster data for row %d of <%s>"),
   176 	                    row, fcb->name);
   177 	            }
Target 0: (r.patch) stopped.
(lldb) gui

In the terminal GUI, it is possible to "walk about" in the threads and frames, examining the variables in each:

Screenshot_lldb

We should perhaps not throw in the towel just yet, but disabling parallelisation when mask is present for seems to me the least bad (i.e. safest) option for 8.3 if it comes to that.

@wenzeslaus
Copy link
Member

From what @petrasovaa showed me, I think the issue is that the same mask file descriptor is used in all threads. The library works just fine reading data from individual rasters in parallel, but the seeking and reading of mask is done using the same file descriptor. So, disabling parallelization when mask is present for v8.3 seems like a good option to me given we want to release soon(ish). Anyway, I'm curious what you think about the source of the issue.

@nilason
Copy link
Contributor

nilason commented Feb 14, 2023

From what @petrasovaa showed me, I think the issue is that the same mask file descriptor is used in all threads. The library works just fine reading data from individual rasters in parallel, but the seeking and reading of mask is done using the same file descriptor. So, disabling parallelization when mask is present for v8.3 seems like a good option to me given we want to release soon(ish). Anyway, I'm curious what you think about the source of the issue.

I'm inclined to believe just that is the root issue: multiple threads are opening and reading the same file. I don't know what mechanism triggers this: is it on GRASS side or the compressor side. The funny thing is that, there is apparently no problem "reading" the file, only "decompressing" what has just been read. In this latest aspect I've given some thought on the global variable extern struct R__ R__ (lib/raster/R.h) . Global variables and parallel programming are not always best friends... so far haven't found direct cause for this being the culprit.

@wenzeslaus
Copy link
Member

...there is apparently no problem "reading" the file, only "decompressing" what has just been read.

While I don't have exact explanation, I think that's because you can read using the file descriptor just fine, but what is read is garbage from point of view of the other thread.

...some thought on the global variable extern struct R__ R__...so far haven't found direct cause for this being the culprit.

My understanding is, and I think it also stems from @aaronsms analysis of the code, that, given how the code is written, this is only a problem when opening the rasters. When reading the rasters, the per-raster globals are not modified, only read, so everything is okay except the mask which is modified in the sense that seek and read all called by all threads.

@nilason
Copy link
Contributor

nilason commented Feb 14, 2023

My understanding is, and I think it also stems from @aaronsms analysis of the code, that, given how the code is written, this is only a problem when opening the rasters. When reading the rasters, the per-raster globals are not modified, only read, so everything is okay except the mask which is modified in the sense that seek and read all called by all threads.

R__ contains an array of all open files (struct fileinfo *fileinfo), which in turn has members like cur_row, null_cur_row. By the look of them they might potentially be altered by reading too. I only now learn how these "low-level" GRASS functions work, so I cannot yet tell. I know there are Vlib read functions too that actually modifies the "file info" struct (Map_info if I recall correctly) by only reading.

@wenzeslaus
Copy link
Member

...members like cur_row, null_cur_row. By the look of them they might potentially be altered by reading too...

I meant one thread can read one map, although now when I'm saying that I realize that in r.patch multiple rasters are read by multiple threads.

@nilason
Copy link
Contributor

nilason commented Feb 14, 2023

Only, the MASK raster is read together with every raster and therefore it is read concurrently in multiple threads.

@nilason
Copy link
Contributor

nilason commented Feb 17, 2023

It is indeed, as I suspected, the case that the global variable R__.fileinfo[x].cur_row is set and read during iterating the rows during file read.

Here it is set (fcb = R__.fileinfo[x]) :

get_map_row_nomask():

fcb->cur_row = r;

and here it is read (row = cur_row):

read_data_compressed():

grass/lib/raster/get_row.c

Lines 133 to 134 in 1bb3943

off_t t1 = fcb->row_ptr[row];
off_t t2 = fcb->row_ptr[row + 1];

Concurrent reading of MASK in multiple threads will obviously lead to very unexpected results. Fixing this is, as I see it, a non-trivial task, I'd suggest to merge #2829 for 8.3 release.

@wenzeslaus wenzeslaus added this to the 8.3.0 milestone Feb 17, 2023
@petrasovaa
Copy link
Contributor

Merged and backported. Should we create a new, more readable ticket and close this one?

@wenzeslaus
Copy link
Member

I consider this bug fixed. The rest is an enhancement.

@nilason
Copy link
Contributor

nilason commented Feb 21, 2023

@ericrpatton Thanks for the report and your valuable assistance in tracking this issue down!

@ericrpatton
Copy link
Author

@ericrpatton Thanks for the report and your valuable assistance in tracking this issue down!

You're welcome. So the verdict on this bug is a wontfix?

@nilason
Copy link
Contributor

nilason commented Feb 23, 2023

@ericrpatton Thanks for the report and your valuable assistance in tracking this issue down!

You're welcome. So the verdict on this bug is a wontfix?

Yes, that is unfortunately the case.

@wenzeslaus
Copy link
Member

I would say this was fixed in #2829. The error no longer occurs. Now a new valid feature request would be for mask and parallel support to work together. Wontfix sounds like we did nothing and plan nothing which is not the case.

@ericrpatton
Copy link
Author

ericrpatton commented Feb 24, 2023

I would say this was fixed in #2829. The error no longer occurs. Now a new valid feature request would be for mask and parallel support to work together. Wontfix sounds like we did nothing and plan nothing which is not the case.

Sorry, I hadn't seen the fix; I re-synced my git checkout and I can confirm the error no long occurs - thanks for that, much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working C Related code is in C
Projects
None yet
Development

No branches or pull requests

5 participants