写程序,从看懂报错开始

【杂谈】写程序,从看懂报错开始

从编译报错,到程序运行出错,经常会收到各种各样的求助或者反馈,有的是使用我的device源码编译系统出错的,亦或者是自己发布的ROM或软件什么的出现问题用户反馈的,还有作业之类的上机题出错的……

所有的反馈也好,提问求助也好,有一样东西是必须的,那就是日志,编译出错得提供编译日志的出错部分,系统运行故障等需要抓取log一并提交,这似乎是不需要再多说的内容,然而在接二连三收到各种各样无效的报告、无效的求助、以及日志已经指名的显而易见的错误和异常原因以后,我才发现,原来有不少的开发者(或学生)并不具备基本的对日志进行判读的能力。本文也将对此进行进一步的讨论。

 

什么是无效报告/求助

       总的来说,无效的报告和求助,通常是由于求助者对问题的阐述不清而导致的,比如求助时为能够在求助中说明其诉求,问题的情境等,错误报告为能够说明清楚异常出现的情境和异常的复现方法等等。然而在更多情况下,无效反馈的产生原因是提交了完全没有价值的日志,甚至是重要问题完全没有日志文件。

 

无效反馈实例与正确反馈做法

下面我将举若干个由于日志问题而导致反馈无效的例子,并会逐个讨论其正确的反馈方式:

对方:我用了你提供的device和内核源码 ,但是编译的时候报错了。

我:日志呢?什么错误

对方:make[3]: *** [xxx] Error 2

make[3]: *** No rule to make target `xxx’, needed by `xxx’.  Stop.

make: *** Waiting for unfinished jobs…

我:没了?

对方:是的

我:……

这里反馈方提供的日志,虽说是导致编译停止的直接原因,但并不是编译出错的根本原因。其根本原因的错误输出呢,在前面呢。总所周知make的话是根据Makefile进行编译的,按照预定的内容去调用编译器或者是其他程序去执行相关的编译操作,并且监测调用的程序返回的结果(main函数的返回值)若是0则是执行成功,若是非零值则会判断为程序执行错误,进而返回如下错误:

make: *** [MODULE_NAME] Error RETURN_VALUE

在这种情况下仅仅是看make抛出的错误提示对排错的帮助并不大,在很多情况下是毫无帮助的,而真正由编译器抛出的错误信息,往往在前面。通常情况下向上翻查就可以找到原本错误的信息了。但是在多线程编译下,某一线程出错时可能会导致错误信息被其他线程的输出所淹没,比如曾经有一个模块在编译的时候出错,此时电脑又在编译着内核,结果等到编译终止时,错误信息早已被编译内核的输出所淹没了。因此,make的话在出现错误时,最好使用单线程重新编译一次,再把出错有用的日志提交。

 

用户:湖南电信用你的ROM有短信音啊。。。

我:请提交log。

(很久以后)

我:人呢?……

先科普一下,在以前开发电信的设备时,短信音是一个特别烦人的东西,这个东西的表现为在特定基站下(据说是中兴的基站?),电话在收到短信后,其听筒会不断发出奇怪的脉冲式类似蜂鸣器的声音,并持续不断直到用户重启设备,这种声音当初我找到了解决方法并写成RIL的framework层模块,用于解决该问题。

对于程序系统错误,尤其是非显而易见的,有强烈地域性的bug,是无论如何都要提供log的,而Android的Log分为两层,一是logcat产生的日志,二是dmesg日志,通常情况下只需要提供logcat的日志即可,由于logcat日志产生速度极快,有时候用的tag也不一定是Error或者Debug,因此通常要求用户提供完整的Log,再由开发者这边筛选。当然,这一类的系统级的bug在有官方运营维护的系统中通常有可视化的一键bug提交机制,这就不需要用户操心了。但是如果使用的是第三方个人或小团队维护的系统的话,就需要用户手动去提交bug了,至于不提交log的反馈,国外开发者社区有一句很有话,那就是“Logcat or GTFO”

直接放截图吧,知乎看到的一个提问:

这个情况下呢,提问者通常的确是提供了可以判别错误的bug了,但是这个编译的错误信息,提问者似乎未对代码进行任何思考就选择求助。

对于编译错误信息的输出,我举几类常见错误代码及其输出为例子吧。

案例一:

源程序:

#include<iostream>

int main(){

cout<<“Hello World!”<<endl;

return 0;

}

输出:

test.cpp:3:5: error: use of undeclared identifier ‘cout’; did you mean

      ‘std::cout’?

cout<<“Hello World!”<<endl;

    ^~~~

std::cout

/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/iostream:54:33: note:

‘std::cout’ declared here

extern _LIBCPP_FUNC_VIS ostream cout;

                                ^

test.cpp:3:27: error: use of undeclared identifier ‘endl’; did you mean

      ‘std::endl’?

cout<<“Hello World!”<<endl;

                          ^~~~

std::endl

/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/ostream:1032:1: note:

‘std::endl’ declared here

endl(basic_ostream<_CharT, _Traits>& __os)

^

2 errors generated.

错误信息是显而易见的:erroruse of undeclared identifier ‘cout’ did you mean  ‘std::cout’?

中文:错误:使用了未定义标识符cout,你是不是指std::cout?

编译器都画公仔画出肠了,就是漏了声明使用那个命名空间,然而这种错误却有不少人选择上网求助……

 

样例二:

程序:

#include<iostream>

using namespace std;

int main(){

cout<<“Hello World!”<<endl;

int a

return 0;

}

输出:

test.cpp:5:10: error: expected ‘;’ at end of declaration

int a

         ^

;

1 error generated.

同样的,也不说什么了,漏了个分号,都提示成这样了,但是网上还是能够找到一大堆这样的求助……

也许,是英语不过关?还是要提高你们的英语知识水平啊。

失败恐惧症?

       提问上述这些显而易见的错误的,通常情况下是一些初学者提出的问题,对于初学者来说,在写程序的时候会对编译出错怀有一种恐惧之心,而不愿意去自己面对错误,进而转向其他人去求助。这某种程度上似乎是失败恐惧症(Or Error恐惧症?)的体现。初学者其实更应该去学习阅读编译器的错误输出,并试图独自进行troubleshoot,这才能够有利于提升自己的水平。

总结

当你在反馈bug、遇到问题询问开发者、或者是纯属是程序设计类作业遇到问题时,学会阅读日志,学会找到日志与错误相关部分尤为重要,也是你能够从他人得到有用反馈的关键。

 

denghaoqing.com与HQ杂谈同步发文

通过CC-BY-NC-SD协议授权规范转载

 

Git入门教程:用Git完成基本的项目提交处理工作

作者:邓昊晴

原文:https://www.denghaoqing.com/?p=173

转载请注明

前言


git是当今应用广泛的一个版本管理工具,相对于以前人们常用的SVN工具,Git工具具有很多的有点。其分布式本地储存代码仓库,让用户随时随地,即使在没有网络的情况下也可以对项目提交修改,开发者在本地拥有完整的项目修改历史。

代码、文档的版本管理在当今团队协作中日益重要,完整的项目修改日志,可撤回的版本管理能够为团队协作提供极大的方便。因此掌握Git工具的使用,是十分必要的。本文将讲述使用Git本地提交代码,提交到远程仓库,以及撤回修改的方法。

继续阅读“Git入门教程:用Git完成基本的项目提交处理工作”

用汇编语言自己写MBR:实现开机前密码验证

重要:本文中所提及的操作会涉及到磁盘主引导记录(MBR)及相邻扇区、磁道的修改,建议您先在虚拟机环境下完成测试,再在实体机进行测试!操作有风险,回车需谨慎!作者不对由于应用本文提及的技术或方法造成的数据等损失负责。

作者:邓昊晴

原载于:www.denghaoqing.com/?p=133

转载请注明以上信息


本文将介绍使用汇编语言编写一个MBR引导程序,以实现在开机进行密码校验并启动的整个设计思路及开发调试过程。其中会涉及到MBR记录、计算机(操作系统)启动过程、磁盘等基本知识。

继续阅读“用汇编语言自己写MBR:实现开机前密码验证”

Ubuntu等Linux系统挂载NTFS分区出错的解决方法

此方法适用于解决在Linux下因提示Metadata kept in Windows cache导致分区无法被挂载的问题。其详细报错信息如下:

Error mounting /dev/sdb6 at /media/sunny/E038493738490DCA: Command-line `mount -t “ntfs” -o “uhelper=udisks2,nodev,nosuid,uid=1000,gid=1000” “/dev/sdb6” “/media/sunny/E038493738490DCA”‘ exited with non-zero exit status 14: The disk contains an unclean file system (0, 0). Metadata kept in Windows cache, refused to mount. Failed to mount ‘/dev/sdb6’: Operation not permitted The NTFS partition is in an unsafe state. Please resume and shutdown Windows fully (no hibernation or fast restarting), or mount the volume read-only with the ‘ro’ mount option.

 

继续阅读“Ubuntu等Linux系统挂载NTFS分区出错的解决方法”

Java Web开发教程(一):创建一个通过Gradle构建的Java Web项目

版权归本博客所有,转载请注明来源与作者:邓昊晴

最近在学习Java Web项目的开发,因此打算在博客中也出一个系列的开发教程,在本次的教程中,我打算涵盖如下方面:

  • Gradle简介
  • 使用Intellij IDEA创建第一个用Gradle构建的Dynamic WEB网站
  • 一个简单的JSP网站
  • 编译、部署、并在浏览器中访问你自己做的网站

这是第一篇文章,我会尽可能详细地去讲创建一个Java动态网页项目的每一个步骤,以便于新手学习。

继续阅读“Java Web开发教程(一):创建一个通过Gradle构建的Java Web项目”

使用免费的第三方机构CA证书让网站使用HTTPS的方法

支持HTTPS的网站在能够提高用户访问的安全性的同时,在某种程度上也可以提高网站在搜索引擎中的索引排名。而且现在互联网安全问题日益受到重视,推广普及HTTPS已经成为了一种趋势(连百度都用https了)

本文将介绍使用Certbot为自己网站生成第三方CA证书的方法

继续阅读“使用免费的第三方机构CA证书让网站使用HTTPS的方法”

The future

转眼间,5年过去了,当我收到域名续费的邮件时才发现时间过得这么快。对域名进行了续费,同时我也打算在注册多一个域名,与本站绑定。

当年还是too young too simple,一心想在服务器上搭discuz建论坛,在未来,方向也许会从当初的毫无方向,到如今的专写博客,一些测试项目也会放在本站。

5年间博客和论坛都遭受到一些来历不明的灌水攻击,造成了服务器资源的浪费,希望以后这类事情不再发生。

今天对网站进行了升级迁移,将blog域名下的wordpress转移到新的服务器上,以便获得更高的稳定性。从今以后网站不再是运行在虚拟主机上啦,有独立的操作系统干的东西也更多了。

同时,我还打算写双语博客,提供中英文版本自动切换。

华硕笔记本UX501VW安装ubuntu或其他linux系统手记

新买的笔记本啊你装ubuntu等linux系统一直都不成功,其表现为卡在了启动界面,安装centos的时候也会出现一些小问题,比如无法正常关机,再进行了很久的研究和测试后,终于找到了解决方法。

在进入安装程序之前,用acpi=no的内核参数启动即可进入系统,但这会导致触摸板等失灵,待安装完毕后,进入系统,在驱动界面里面安装nvidia官方驱动,安装好以后,即可修改/boot/grub/grub.cfg,删除acpi=no参数,进入系统,此后系统就可以完美运行了。

同步安卓源码连不上谷歌的解决办法

首先想到的肯定是通过hosts,VPN等方式连接上谷歌,同步源码,但是速度缓慢。

然后我找到了一个更为有效的解决方法,清华大学架设了一个提供googlesource上的Android(AOSP)的代码镜像,可以很大程度解决目前网络问题,而且速度还不错。

清华大学tuna镜像源 aosp即可找到。

Error with audio_policy

Microphone is not working.According the log, error occured because:

E/AudioRecord( 151): Could not get audio input for record source 1, sample rate 8000, format 0x1, channel mask 0x10, session 29, flags 0