7

Tôi đã gặp sự cố với sự cố do trả lại giá trị của bitet khi bitet lớn. Đây có phải là một lỗi trình biên dịch hoặc tôi đã nhầm lẫn thực hiện một cái gì đó gây ra hành vi không xác định?Lỗi Seg trên GCC4.6 khi cố di chuyển một bitet lớn, đây có phải là lỗi trình biên dịch không?

Mã bên dưới bị treo trên GCC 4.6.3 với cờ -std=c++0x được đặt.

#include <bitset> 

// typedef std::bitset<0xffff> uut; 
typedef std::bitset<0xffffff> uut; 

struct foo { 
    foo(uut b) 
    : b_(std::move(b)) 
    { 
    } 

    uut b_; 
}; 

uut make_bits(int) 
{ 
    uut bits; 

    // Only works for 0xffff: 
    return std::move(bits); 
    // Works for both 0xffff and 0xffffff: 
    //return bits; 
} 

int main() 
{ 
    foo(make_bits(0)); 
} 

Thông thường, nếu tôi xóa tham số int thì có thể khiến chức năng được gạch chân không?

Như @unwind gợi ý, đây là đầu ra chạy theo valgrind ./a.out:

==24780== Memcheck, a memory error detector 
==24780== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==24780== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 
==24780== Command: ./a.out 
==24780== 
==24780== Warning: client switching stacks? SP change: 0x7ff000068 --> 0x7fea00058 
==24780==   to suppress, use: --max-stackframe=6291472 or greater 
==24780== Invalid write of size 8 
==24780== at 0x4005E5: main (in /home/sam/scratch/a.out) 
==24780== Address 0x7fea00058 is on thread 1's stack 
==24780== 
==24780== Warning: client switching stacks? SP change: 0x7fea00050 --> 0x7fe800040 
==24780==   to suppress, use: --max-stackframe=2097168 or greater 
==24780== Invalid write of size 8 
==24780== at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out) 
==24780== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24780== Address 0x7fe800048 is on thread 1's stack 
==24780== 
==24780== 
==24780== Process terminating with default action of signal 11 (SIGSEGV) 
==24780== Access not within mapped region at address 0x7FE800048 
==24780== at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out) 
==24780== If you believe this happened as a result of a stack 
==24780== overflow in your program's main thread (unlikely but 
==24780== possible), you can try to increase the size of the 
==24780== main thread stack using the --main-stacksize= flag. 
==24780== The main thread stack size used in this run was 8388608. 
==24780== 
==24780== Process terminating with default action of signal 11 (SIGSEGV) 
==24780== Access not within mapped region at address 0x7FE800039 
==24780== at 0x4A255A0: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so) 
==24780== If you believe this happened as a result of a stack 
==24780== overflow in your program's main thread (unlikely but 
==24780== possible), you can try to increase the size of the 
==24780== main thread stack using the --main-stacksize= flag. 
==24780== The main thread stack size used in this run was 8388608. 
==24780== 
==24780== HEAP SUMMARY: 
==24780==  in use at exit: 0 bytes in 0 blocks 
==24780== total heap usage: 0 allocs, 0 frees, 0 bytes allocated 
==24780== 
==24780== All heap blocks were freed -- no leaks are possible 
==24780== 
==24780== For counts of detected and suppressed errors, rerun with: -v 
==24780== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2) 

Và với valgrind --max-stacksize=99999999 ./a.out, như valgrind nhắc tôi:

==24790== Memcheck, a memory error detector 
==24790== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==24790== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 
==24790== Command: ./a.out 
==24790== 
==24790== Warning: client switching stacks? SP change: 0x7ff000068 --> 0x7fea00058 
==24790==   to suppress, use: --max-stackframe=6291472 or greater 
==24790== Invalid write of size 8 
==24790== at 0x4005E5: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fea00058 is on thread 1's stack 
==24790== 
==24790== Warning: client switching stacks? SP change: 0x7fea00050 --> 0x7fe800040 
==24790==   to suppress, use: --max-stackframe=2097168 or greater 
==24790== Invalid write of size 8 
==24790== at 0x40056F: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800048 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 4 
==24790== at 0x400576: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800044 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 8 
==24790== at 0x400590: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800038 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 4 
==24790== at 0x4C2E0E0: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400594: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800050 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 4 
==24790== at 0x4C2E0EB: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400594: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800058 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2E10E: memset (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800038 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4005A7: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800048 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 8 
==24790== at 0x4C2D10D: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fee00058 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D11A: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe9fffc8 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D108: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x4005C0: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe9fffc0 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4005C1: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4005E9: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fe800048 is on thread 1's stack 
==24790== 
==24790== Warning: client switching stacks? SP change: 0x7fe800040 --> 0x7fea00050 
==24790==   to suppress, use: --max-stackframe=2097168 or greater 
==24790==   further instances of this message will not be shown. 
==24790== Invalid read of size 8 
==24790== at 0x4005C9: make_bits(int) (in /home/sam/scratch/a.out) 
==24790== by 0x4E5376C: (below main) (libc-start.c:226) 
==24790== Address 0x7fea00058 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D000: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x40060A: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fec00060 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 8 
==24790== at 0x4C2D004: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x40060A: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fea00060 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D00F: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x40060A: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fec00070 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D108: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out) 
==24790== by 0x400612: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fec00058 is on thread 1's stack 
==24790== 
==24790== Invalid read of size 8 
==24790== at 0x4C2D11A: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out) 
==24790== by 0x400612: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7fec00048 is on thread 1's stack 
==24790== 
==24790== Invalid write of size 8 
==24790== at 0x4C2D10D: [email protected]@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==24790== by 0x400650: foo::foo(std::bitset<16777215ul>) (in /home/sam/scratch/a.out) 
==24790== by 0x400612: main (in /home/sam/scratch/a.out) 
==24790== Address 0x7feffffe0 is on thread 1's stack 
==24790== 
==24790== 
==24790== HEAP SUMMARY: 
==24790==  in use at exit: 0 bytes in 0 blocks 
==24790== total heap usage: 0 allocs, 0 frees, 0 bytes allocated 
==24790== 
==24790== All heap blocks were freed -- no leaks are possible 
==24790== 
==24790== For counts of detected and suppressed errors, rerun with: -v 
==24790== ERROR SUMMARY: 2097097 errors from 19 contexts (suppressed: 2 from 2) 
+1

Bitet đó là _huge_! Bạn có biết rằng nó chứa hơn 2 megabyte giá trị của bit? –

+4

Tôi muốn nói rằng ngăn xếp ngăn xếp của nó. – Flexo

+0

@Lightness Races trong Orbit: yup! :) –

Trả lời

2

Chúng ta có thể nhìn thấy chính xác những gì GCC đang làm dưới mui xe bằng cách biên dịch cả hai trường hợp với -S:

g++-4.6 -std=c++0x test.cc -S -fverbose-asm 

Và t hen sử dụng diff để so sánh các kết quả đầu ra:

diff -rNu move.s ret.s |c++filt  
--- move.s 2015-05-21 14:00:49.097524035 +0100 
+++ ret.s 2015-05-21 14:00:40.021510019 +0100 
@@ -79,23 +79,13 @@ 
    .cfi_offset 5, -8 
    movl %esp, %ebp #, 
    .cfi_def_cfa_register 5 
- subl $2097176, %esp #, 
- leal -2097160(%ebp), %eax #, tmp60 
+ subl $24, %esp #, 
+ movl 8(%ebp), %eax # .result_ptr, tmp59 
    movl $2097152, %edx #, tmp61 
    movl %edx, 8(%esp) # tmp61, 
    movl $0, 4(%esp) #, 
    movl %eax, (%esp) # tmp60, 
    call memset # 
- leal -2097160(%ebp), %eax #, tmp64 
- movl %eax, (%esp) # tmp64, 
- call std::remove_reference<std::bitset<16777215u>&>::type&& std::move<std::bitset<16777215u>&>(std::bitset<16777215u>&) # 
- movl %eax, %edx #, D.21547 
- movl 8(%ebp), %eax # .result_ptr, tmp65 
- movl $2097152, %ecx #, tmp68 
- movl %ecx, 8(%esp) # tmp68, 
- movl %edx, 4(%esp) # tmp67, 
- movl %eax, (%esp) # tmp66, 
- call memcpy # 
    movl 8(%ebp), %eax # .result_ptr, 
    leave 
    .cfi_restore 5 

(Các dòng được đánh dấu + chỉ tồn tại trong trường hợp trả về, các dòng có - chỉ tồn tại trong trường hợp di chuyển).

Có rất nhiều thao tác con trỏ ngăn xếp khác đang diễn ra trong trường hợp di chuyển (và một số số rất lớn ở đó). Điều quan trọng là sau đó kết thúc với một cuộc gọi memcpy sao chép các kết quả trở lại vào ngăn xếp. Phân tích của tôi về nó là trường hợp trả về theo giá trị thực sự có một sự tối ưu khác xảy ra, có nghĩa là không sử dụng tạm thời bên trong chính bị bỏ qua hoàn toàn cho trường hợp trả về theo giá trị, nhưng không phải là trường hợp di chuyển.

Chúng tôi có thể khẳng định rằng hơn nữa bằng cách thực hiện các phân tích tương tự về sự trở lại của trường hợp giá trị với -O0 vô hiệu hóa tất cả optimsisations và nhìn thấy những gì xảy ra:

diff -Nru noopt.s ret.s 
--- noopt.s 2015-05-21 14:06:14.798028762 +0100 
+++ ret.s 2015-05-21 14:00:40.021510019 +0100 
@@ -3,7 +3,7 @@ 
# compiled by GNU C version 4.6.4, GMP version 5.1.3, MPFR version 3.1.2-p3, MPC version 1.0.1 
# GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 
# options passed: -imultilib . -imultiarch i386-linux-gnu -D_GNU_SOURCE 
-# test.cc -mtune=generic -march=i686 -O0 -std=c++0x -fverbose-asm 
+# test.cc -mtune=generic -march=i686 -std=c++0x -fverbose-asm 
# -fstack-protector 
# options enabled: -fasynchronous-unwind-tables -fauto-inc-dec 
# -fbranch-count-reg -fcommon -fdelete-null-pointer-checks -fdwarf2-cfi-asm 
@@ -79,23 +79,13 @@ 
    .cfi_offset 5, -8 
    movl %esp, %ebp #, 
    .cfi_def_cfa_register 5 
- subl $2097176, %esp #, 
- leal -2097160(%ebp), %eax #, tmp60 
+ subl $24, %esp #, 
+ movl 8(%ebp), %eax # .result_ptr, tmp59 
    movl $2097152, %edx #, tmp61 
    movl %edx, 8(%esp) # tmp61, 
    movl $0, 4(%esp) #, 
    movl %eax, (%esp) # tmp60, 
    call memset # 
- leal -2097160(%ebp), %eax #, tmp64 
- movl %eax, (%esp) # tmp64, 
- call _ZSt4moveIRSt6bitsetILj16777215EEEONSt16remove_referenceIT_E4typeEOS4_ # 
- movl %eax, %edx #, D.21547 
- movl 8(%ebp), %eax # .result_ptr, tmp65 
- movl $2097152, %ecx #, tmp68 
- movl %ecx, 8(%esp) # tmp68, 
- movl %edx, 4(%esp) # tmp67, 
- movl %eax, (%esp) # tmp66, 
- call memcpy # 
    movl 8(%ebp), %eax # .result_ptr, 
    leave 
    .cfi_restore 5 

Một lần nữa có cùng chồng con trỏ manipluation và sao chép xảy ra với optimisations bị vô hiệu hóa trong trường hợp trả về theo giá trị. Vì vậy, có vẻ như bạn đã có tràn ngăn xếp trong cả hai trường hợp, nhưng trong trường hợp trả về theo giá trị, trường hợp thử nghiệm của bạn không đủ để thực sự quan sát nó vì các tối ưu khác.

Giải pháp: phân bổ trên heap hoặc nhận một ngăn xếp lớn hơn bằng cách sử dụng pthread_attr_setstacksize hoặc clone trên Linux.

Các vấn đề liên quan