Category Archives: G_Tips

记两个gcc的bug

一般来说程序的编译、运行遇到问题,只会怀疑code有问题,而不会怀疑是编译器的问题。不过日子久了,总会遇到各种各样奇怪的问题,最近遇到了两个gcc相关的bug,记录一下。

问题一

在编译一个并不复杂的文件的时候,gcc报内存不够的错误。
这个文件只是定义了一个嵌套了几层的std::map,而且,如果用GCC 4.8来编译,很快就编译完了,占的内存也不多。
然而,如果用GCC 6.2 (或者最新的7.1)来编译,会发现占用的时间非常长,而且占用内存特别多,最终(如果内存不够)会报internal compiler error: Killed的错。
观察发现,在编译的过程中gcc占用的内存越来越多,分配了足够多的swap之后能编译成功,最终内存要占到16~20GiB左右。
同事去submit了一个bug,https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80290,被confirm了。
不过,从comments看,似乎还挺复杂的,什么时候能修好,就难说了。。。
另外,用clang的话也不会出现这个bug。

问题二

在一个动态库里,遇到一个std::unique_ptr相关的segment fault。这个变量在constructor里明明创建过了,但是运行时发现它还是nullptr
简化一下问题,如果一个动态库里有类似这样的code:

std::unique_ptr<MyStruct> s = nullptr;
void onLoad() __attribute__((constructor));
void onLoad()
{
  s = std::make_unique<MyStruct>();
}

当这个动态库被dlopen的时候,我期望s会被onLoad()初始化好,然而在别的函数里用这个s的时候,会segment fault,因为s里面是个空指针!
关于这个问题,我在SO上提了个问题,结果发现,跟这个GCC的这个bug有关:

简单的说,动态库里 constructor 和 global/static 变量的初始化顺序是unspecified,所以有可能onLoad()先执行,然后再初始化global变量s, 把它初始化成nullptr。注意这个时候只是初始化,原来的变量并不会被析构,其实也是内存泄露。
不过,这个bug看上去暂时不会被修复,只是会在文档里注明这种情况初始化顺序是不定的。
要修复这个问题,需要给变量指定初始化顺序:

std::unique_ptr<MyStruct> s __attribute__((init_priority(101))) = nullptr;

注意init_priority的值设为101~65534都可以。 0~100是reserved,用了会有warning; 最大值是65535,也是默认值,要是设成65535,就和__attribute__((constructor))一样,顺序就变成unspecified了。
另外,clang也支持这个属性,表现也gcc一样,有同样的bug。

Share

在墙内用gmail两步认证发送kernel patch

Linux Kernel的开发,都是通过邮件发patch的形式来review的。如果用自己的邮件服务器当然没问题。
不过如果想用gmail帐号来发patch,必须翻墙才行;
如果你的gmail帐号开通了两步认证(如果没有,建议开通),还需要额外的App-Specific密码。

这里介绍一个简便的方法。

前提

有一个翻墙的方式,比如Shadowsocks
开通了gmail两步认证。

原理

  • 配置git使用msmtp来发送邮件;
  • 通过proxychains来翻墙;
  • 通过App-Specific密码来登录Google的SMTP server

步骤(以shadowsocks为例)

  1. 安装msmtp, proxychains
    sudo apt-get install msmtp proxychains
    
  2. 配置msmtp使用gmail的SMTP服务
    $ cat ~/.msmtprc
    # Example for a user configuration file
    # Set default values for all following accounts.
    defaults
    tls on
    tls_trust_file /etc/ssl/certs/ca-certificates.crt
    logfile ~/msmtp.log
    # My email service
    account gmail
    host smtp.gmail.com
    port 587
    from <your-gmail-id>@gmail.com
    auth on
    user <your-gmail-id> @gmail.com
    password
    # Set a default account
    account default : gmail
    
  3. 配置proxychains使用shadowsocks
    $ cat /etc/proxychains.conf
    ...
    [ProxyList]
    # add proxy here ...
    socks5 127.0.0.1 1080 ## Set socks5 proxy to your sslocal ip/port
    
  4. 配置App-Specific Password
    1. 登录到https://security.google.com/settings/security/apppasswords
    2. Generate一个密码,建议设置一个custom name,比如说linux-kernel
    3. 这个密码一般是16个ascii码,保留好这个密码,建议保存在KeePass里。
  5. 生成git patch并发送邮件
    # 生成patch
    # 生成patch
    git format-patch --to=<kernel-mailing-list> --cc=<reviewer> --subject-prefix="PATCH linux" <revision range>
    
    # 启动shadowsocks
    sslocal -c <your-ss-config.json>&
    
    # 通过proxychains来发送邮件
    proxychains git send-email <your patches>.patch
    

    当它提示要输密码的时候,不要输gmail的密码,输入之前generate的linux-kernel的密码

    password for <your-gmail-id>@gmail.com at smtp.gmail.com:
    

Done!

Share

在Android 7.0上解决感叹号问题 [更新至7.1.1]

背景

从Android 5.0 (Lollipop)开始,安卓为了检测各种需要登录的Wifi服务,提供了captive_portal_detection_enabledcaptive_portal_server这两个设置,分别作为检测的开关和服务器。原理是访问http://<server>/generate_204这个URL:

  • 如果返回204,就说明wifi可以直接连接,不需要登录;
  • 如果返回非204并且非空的内容,说明wifi需要登录。

默认的服务器是被墙的google的服务器,所以总是检测不正确,导致出来感叹号。

具体情况可以参看小狐狸的这篇文章,里面详细介绍了原理,以及如何设置墙内的服务器。

问题

不过,从Android 7.0 (Nougat)开始,这个设置稍微有点变化,在某些服务器上会导致又出现感叹号,这里介绍一下。

Android 7.0 引入了一个新的设置:CAPTIVE_PORTAL_USE_HTTPS(captive_portal_use_https),默认值是1,也就是说,默认情况,它会用https://<server>/generate_204这个URL来判断isCaptivePortal()
具体的code请移步NetworkMonitor.java#285

假如原来的服务器不支持HTTPS,或者支持了HTTPS但是这个URL没有返回204,都会导致感叹号的问题。

解决

所以解决问题的方式也很简单:

  • 要么配置墙内的服务器,让它在https的情况也也返回204;
  • 要么在手机上把HTTPS的值设为0 (settings put global captive_portal_use_https 0)

比如说,V2EX刚提供了captive.v2ex.co这个URL,同时支持HTTP和HTTPS。
本站也提供http://ping.mine260309.me/generate_204https://mine260309.me/generate_204这两个URL。

更新

升级到7.1.1之后,会发现感叹号(或者x号)又出现了。
感谢@tedjiang的comment,解决办法是:

settings put global captive_portal_https_url https://captive.v2ex.co/generate_204
Share

用gzip 1.7+配合rsync实现备份的快速传输

Backround

定期备份VPS的特定目录是一个常见的需求。一般来说,备份分为如下步骤:

  1. 会用tar来打包
  2. 用压缩工具(如gzip/bzip2/xz)压缩
  3. rsync来传输备份文件

比如说我定期备份博客目录的命令如下:

tar -cf - --exclude=/srv/www/wordpress/logs --exclude=/srv/www/wordpress/wp-content/cache /srv/www/wordpress/ | gzip - > ~/backup/wordpresss.bak.tar.gz

然后有另外的rsync脚本传输wordpresss.bak.tar.gz

Problem

只要wordpress里的任何一个文件/目录有任何改动,比如说时间戳变了,每次备份产生的tar.gz都完全不一样,这样rsync传输文件的时候每次都需要完整传输几乎所有的内容,即浪费时间也浪费流量。
比如说,我的博客备份的一次完整的传输在网络条件一般的情况下可能需要几十分钟!

Solution

gzip1.7开始正式提供 --rsyncable选项,使产生的压缩包对rsync更友好,方便rsync作delta传输。
虽然VPS上只有1.6版本,但是开源的好处就是大家可以自己编译:

  1. 到 https://ftp.gnu.org/gnu/gzip/ 下载1.7版本的gzip(最新的已经是1.8了)
  2. configure, make and install
  3. 添加--rsyncable选项到备份的命令里:
    tar -cf - --exclude=/srv/www/wordpress/logs --exclude=/srv/www/wordpress/wp-content/cache /srv/www/wordpress/ | gzip --rsyncable - > ~/backup/wordpresss.bak.tar.gz
    

Result

效果很明显,rsync的传输时间从几(十)分钟减少到几十秒钟,speedup大约在700左右。
节省了时间和流量 🙂

Share

Enable HTTP2 for WordPress on nginx Ubuntu 14.04

HTTP2 is enabled on mainline version of nginx (v1.9.x), while Ubuntu 14.04 is using nginx 1.4.6.
To enable HTTP2, the simplest way is to upgrade nginx to the mainline version.
But I met several issues during the upgrade, and here’re the steps, the issues and solutions.

Upgrade to mainline nginx

Let’s use nginx’s official pre-built packages.

  • Add below lines into /etc/apt/sources.list.d/nginx.list
    sudo sh -c 'echo deb http://nginx.org/packages/mainline/ubuntu/ trusty nginx >> /etc/apt/sources.list.d/nginx.list'
    sudo sh -c 'echo deb-src http://nginx.org/packages/mainline/ubuntu/ trusty nginx >> /etc/apt/sources.list.d/nginx.list'
    
  • Add nginx apt key
    wget -q -O- http://nginx.org/keys/nginx_signing.key | sudo apt-key add -
    
  • Update ans install nginx
    sudo apt-get update
    sudo apt-get install nginx
    

However, it fails with error that it tries to overwrite nginx-common’s file:

dpkg: error processing archive /var/cache/apt/archives/nginx_1.9.11-1~trusty_amd64.deb (--unpack):
 trying to overwrite '/usr/share/nginx/html/index.html'

The solution is to remove old nginx on Ubuntu and re-install mainline nginx.

sudo apt-get remove nginx-common
sudo apt-get install nginx

Now nginx is upgraded to 1.9.11

Config file changes

  1.   Previously the site config files are stored in /etc/nginx/site-available, and the enabled sites are soft-linked in /etc/nginx/site-enabled/.
    But mainline nginx only load config files in /etc/nginx/conf.d/*.conf.
    So either copy the old config files to conf.d, or add include /etc/nginx/sites-enabled/ in nginx.conf.
  2. Mainline nginx’s user is nginx, but Ubuntu uses www-data, so change it in nginx.conf.
    #user nginx;
    user www-data;
    
  3. Change spdy to http2 in site config.

Reload nginx, and now the site’s HTTP2 is working.
However, try to open any page of WordPress, it just shows a blank page without any error. The log shows HTTP 200 OK.

Blank page issue

After googling the issue, it’s found that a missing FastCGI param is the root cause.
Add below line in /etc/nginx/fastcgi_params:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Reload nginx again, and now everything works fine.

Share