标签: x86-64

"无法解释的"核心转储

我一生中见过许多核心垃圾场,但这一次让我难过.

语境:

  • AMD Barcelona CPU 集群上运行的多线程Linux/x86_64程序
  • 崩溃的代码执行得很多
  • 在负载下运行1000个程序实例(完全相同的优化二进制文件)每小时产生1-2次崩溃
  • 崩溃发生在不同的机器上(但机器本身非常相同)
  • 崩溃看起来都一样(相同的确切地址,相同的调用堆栈)

以下是崩溃的详细信息:

Program terminated with signal 11, Segmentation fault.
#0  0x00000000017bd9fd in Foo()
(gdb) x/i $pc
=> 0x17bd9fd <_Z3Foov+349>: rex.RB orb $0x8d,(%r15)

(gdb) x/6i $pc-12
0x17bd9f1 <_Z3Foov+337>:    mov    (%rbx),%eax
0x17bd9f3 <_Z3Foov+339>:    mov    %rbx,%rdi
0x17bd9f6 <_Z3Foov+342>:    callq  *0x70(%rax)
0x17bd9f9 <_Z3Foov+345>:    cmp    %eax,%r12d
0x17bd9fc <_Z3Foov+348>:    mov    %eax,-0x80(%rbp)
0x17bd9ff <_Z3Foov+351>:    jge    0x17bd97e <_Z3Foov+222>
Run Code Online (Sandbox Code Playgroud)

您会注意到崩溃发生在指令中间0x17bd9fc,即从调用返回0x17bd9f6到虚函数之后.

当我检查虚拟表时,我发现它没有以任何方式损坏:

(gdb) x/a $rbx
0x2ab094951f80: 0x3f8c550 <_ZTI4Foo1+16>
(gdb) x/a 0x3f8c550+0x70
0x3f8c5c0 <_ZTI4Foo1+128>:  0x2d3d7b0 …
Run Code Online (Sandbox Code Playgroud)

linux x86-64 segmentation-fault

27
推荐指数
2
解决办法
1435
查看次数

x86程序集pushl/popl不能与"错误:后缀或操作数无效"

我是汇编编程的新手,在GNU汇编程序v2.20.1的Ubuntu x86_64桌面上使用Programming Ground Up.

我已经能够组装/链接执行我的代码,直到我使用pushl/popl指令来操作堆栈.以下代码无法汇编:

 .section .data  # empty

 .section .text
.globl _start
_start:
 pushl $1       # push the value 1 onto the stack
 popl %eax      # pop 1 off the stack and into the %eax register
 int $0x80      # exit the program with exit code '1'
Run Code Online (Sandbox Code Playgroud)

使用"as test.s -o test.o",这些错误出现在终端上并且未创建test.o:

test.s: Assembler messages: 
test.s:9: Error: suffix or operands invalid for 'push'
test.s:10:  Error: suffix or operands invalid for 'popl'
Run Code Online (Sandbox Code Playgroud)

我检查了文档,我用于pushl和popl的操作数是有效的.这不是一个调试问题 - 所以我的代码出了什么问题?或者是我的汇编程序?

x86 assembly x86-64

27
推荐指数
3
解决办法
3万
查看次数

为什么GCC会生成mov%eax,%eax以及它是什么意思?

GCC 4.4.3生成了以下x86_64程序集.令我困惑的部分是mov %eax,%eax.将寄存器移到自身?为什么?

   23b6c:       31 c9                   xor    %ecx,%ecx        ; the 0 value for shift
   23b6e:       80 7f 60 00             cmpb   $0x0,0x60(%rdi)  ; is it shifted?
   23b72:       74 03                   je     23b77
   23b74:       8b 4f 64                mov    0x64(%rdi),%ecx  ; is shifted so load shift value to ecx
   23b77:       48 8b 57 38             mov    0x38(%rdi),%rdx  ; map base
   23b7b:       48 03 57 58             add    0x58(%rdi),%rdx  ; plus offset to value
   23b7f:       8b 02                   mov    (%rdx),%eax      ; load map_used value to eax …
Run Code Online (Sandbox Code Playgroud)

assembly gcc x86-64

27
推荐指数
1
解决办法
9194
查看次数

x86-64汇编的性能优化 - 对齐和分支预测

我目前编码的一些C99标准库字符串函数高度优化的版本,例如strlen(),memset()等等,采用x86-64的组件,SSE-2指令.

到目前为止,我已经在性能方面取得了很好的成绩,但是当我尝试优化更多时,我有时会遇到奇怪的行为.

例如,添加或甚至删除一些简单的指令,或者只是重新组织一些用于跳转的本地标签会完全降低整体性能.在代码方面绝对没有理由.

所以我的猜测是代码对齐存在一些问题,和/或有错误预测的分支.

我知道,即使使用相同的架构(x86-64),不同的CPU也有不同的分支预测算法.

但是,在开发x86-64的高性能时,是否存在一些关于代码对齐和分支预测的一般建议?

特别是关于对齐,我应该确保跳转指令使用的所有标签都在DWORD上对齐吗?

_func:
    ; ... Some code ...
    test rax, rax
    jz   .label
    ; ... Some code ...
    ret
    .label:
        ; ... Some code ...
        ret
Run Code Online (Sandbox Code Playgroud)

在前面的代码中,我之前应该使用align指令.label:,例如:

align 4
.label:
Run Code Online (Sandbox Code Playgroud)

如果是这样,使用SSE-2时是否足以对齐DWORD?

关于分支预测,是否有一种"优先"的方式来组织跳转指令使用的标签,以帮助CPU,或者今天的CPU是否足够聪明,可以通过计算分支的计数来确定在运行时?

编辑

好的,这是一个具体的例子 - 这是strlen()SSE-2 的开始:

_strlen64_sse2:
    mov         rsi,    rdi
    and         rdi,    -16
    pxor        xmm0,   xmm0
    pcmpeqb     xmm0,   [ rdi ]
    pmovmskb    rdx,    xmm0
    ; ...
Run Code Online (Sandbox Code Playgroud)

使用1000个字符串运行10'000'000次约为0.48秒,这很好.
但它不会检查NULL字符串输入.显然,我会添加一个简单的检查:

_strlen64_sse2:
    test       rdi,    rdi
    jz          .null
    ; ... …
Run Code Online (Sandbox Code Playgroud)

performance assembly x86-64 sse2 branch-prediction

27
推荐指数
3
解决办法
5817
查看次数

英特尔失去了周期?rdtsc和CPU_CLK_UNHALTED.REF_TSC之间的不一致

在最近的CPU上(至少在过去十年左右),除了各种可配置的性能计数器之外,英特尔还提供了三个固定功能硬件性能计数器.三个固定柜台是:

INST_RETIRED.ANY
CPU_CLK_UNHALTED.THREAD
CPU_CLK_UNHALTED.REF_TSC
Run Code Online (Sandbox Code Playgroud)

第一个计算退役指令,第二个计算实际周期,最后一个是我们感兴趣的."英特尔软件开发人员手册"第3卷的描述如下:

当核心未处于暂停状态而不处于TM停止时钟状态时,此事件计算TSC速率下的参考周期数.核心在运行HLT指令或MWAIT指令时进入暂停状态.此事件不受核心频率变化(例如,P状态)的影响,但计数与时间戳计数器的频率相同.当核心未处于暂停状态而不处于TM stopclock状态时,此事件可以估计经过的时间.

因此,对于CPU绑定循环,我希望该值与从中读取的自由运行TSC值相同rdstc,因为它们应该仅针对暂停的循环指令或"TM stopclock state"是什么发散.

我使用以下循环测试它(整个独立演示在github上可用):

for (int i = 0; i < 100; i++) {
    PFC_CNT cnt[7] = {};

    int64_t start = nanos();
    PFCSTART(cnt);
    int64_t tsc =__rdtsc();
    busy_loop(CALIBRATION_LOOPS);
    PFCEND(cnt);
    int64_t tsc_delta   = __rdtsc() - tsc;
    int64_t nanos_delta = nanos() - start;

    printf(CPU_W "d" REF_W ".2f" TSC_W ".2f" MHZ_W ".2f" RAT_W ".6f\n",
            sched_getcpu(),
            1000.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC] / nanos_delta,
            1000.0 * tsc_delta / nanos_delta,
            1000.0 * CALIBRATION_LOOPS / nanos_delta,
            1.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]/tsc_delta); …
Run Code Online (Sandbox Code Playgroud)

performance x86 x86-64 cpu-architecture rdtsc

27
推荐指数
1
解决办法
1300
查看次数

这个没有 libc 的 C 程序如何工作?

我遇到了一个没有 libc 的最小 HTTP 服务器:https : //github.com/Francesco149/nolibc-httpd

我可以看到定义了基本的字符串处理函数,导致write系统调用:

#define fprint(fd, s) write(fd, s, strlen(s))
#define fprintn(fd, s, n) write(fd, s, n)
#define fprintl(fd, s) fprintn(fd, s, sizeof(s) - 1)
#define fprintln(fd, s) fprintl(fd, s "\n")
#define print(s) fprint(1, s)
#define printn(s, n) fprintn(1, s, n)
#define printl(s) fprintl(1, s)
#define println(s) fprintln(1, s)
Run Code Online (Sandbox Code Playgroud)

基本的系统调用在 C 文件中声明:

size_t read(int fd, void *buf, size_t nbyte);
ssize_t write(int fd, const void *buf, size_t nbyte);
int open(const char *path, int flags); …
Run Code Online (Sandbox Code Playgroud)

c assembly x86-64 system-calls abi

27
推荐指数
2
解决办法
1634
查看次数

Intel x86 vs x64系统调用

我正在阅读x86和x64之间的汇编差异.

在x86上,系统调用号被放入eax,然后int 80h执行以生成软件中断.

但是在x64上,系统调用号被放入rax,然后syscall被执行.

我被告知,syscall它比生成软件中断更轻,更快.

为什么它在x64上比x86更快,我可以使用x64进行系统调用int 80h吗?

linux x86 assembly x86-64 interrupt

26
推荐指数
2
解决办法
9436
查看次数

为什么C ++编译器可能会复制一个函数退出基本块?

考虑以下代码片段:

int* find_ptr(int* mem, int sz, int val) {
    for (int i = 0; i < sz; i++) {
        if (mem[i] == val) { 
            return &mem[i];
        }
    }
    return nullptr;
}
Run Code Online (Sandbox Code Playgroud)

-O3上的GCC将其编译为:

find_ptr(int*, int, int):
        mov     rax, rdi
        test    esi, esi
        jle     .L4                  # why not .L8?
        lea     ecx, [rsi-1]
        lea     rcx, [rdi+4+rcx*4]
        jmp     .L3
.L9:
        add     rax, 4
        cmp     rax, rcx
        je      .L8
.L3:
        cmp     DWORD PTR [rax], edx
        jne     .L9
        ret
.L8:
        xor     eax, eax
        ret
.L4: …
Run Code Online (Sandbox Code Playgroud)

c++ gcc x86-64 compiler-optimization

26
推荐指数
1
解决办法
500
查看次数

是什么导致页面错误?

根据维基百科:

当程序访问映射到虚拟地址空间但未加载到物理内存中的页面时,页面错误是硬件引发的软件陷阱.(强调我的)

好的,这是有道理的.

但如果是这样的话,为什么每当Process Hacker中的流程信息刷新时,我看到大约15页的错误?

截图

或者换句话说,为什么任何内存被分页?(我不知道它是用户还是内核内存.)我没有页面文件,RAM使用量大约是4 GB中的1.2 GB,这是在干净的重新启动之后.没有任何资源; 为什么有什么东西会被淘汰?

windows paging x86 x86-64

25
推荐指数
2
解决办法
5万
查看次数

英特尔x86 0x2E/0x3E前缀分支预测实际使用?

在最新的英特尔软件开发手册中,它描述了两个操作码前缀:

Group 2 > Branch Hints

    0x2E: Branch Not Taken
    0x3E: Branch Taken
Run Code Online (Sandbox Code Playgroud)

这些允许跳转指令的显式分支预测(像操作码一样Jxx)

我记得在几年前读过x86显式分支预测本质上是gccs分支谓词内在函数上下文中的无操作.

我现在还不清楚这些x86分支提示是否是一个新功能,或者它们在实践中是否基本上是无操作.

任何人都可以清除这个吗?

(那就是:gccs分支预测函数会生成这些x86分支提示吗? - 并且当前的Intel CPU不会忽略它们吗? - 这是什么时候发生的?)

更新:

我创建了一个快速测试程序:

int main(int argc, char** argv)
{
    if (__builtin_expect(argc,0))
        return 1;

    if (__builtin_expect(argc == 2, 1))
        return 2;

    return 3;
}
Run Code Online (Sandbox Code Playgroud)

拆卸以下内容:

00000000004004cc <main>:
  4004cc:   55                      push   %rbp
  4004cd:   48 89 e5                mov    %rsp,%rbp
  4004d0:   89 7d fc                mov    %edi,-0x4(%rbp)
  4004d3:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
  4004d7:   8b 45 fc                mov    -0x4(%rbp),%eax
  4004da: …
Run Code Online (Sandbox Code Playgroud)

x86 gcc x86-64 branch-prediction

25
推荐指数
3
解决办法
5626
查看次数