分类目录归档:程序语言

一个网站崩溃问题的解决方法之总结

问题

公司的网站(ASP .NET MVC的架构) 频繁重启,甚至超过进程池的快速失败保护,导致Service Unavailable。

在自己的日志里,只有重启的记录,没法查到哪里出的错。看IIS日志,也没看出什么来。

事件查看器里面,重启的时候有下面的描述:

EventType clr20r3, P1 w3wp.exe, P2 6.0.3790.3959, P3 45d6968e, P4 system.data.linq, P5 3.5.0.0, P6 488ef99a, P7 92c, P8 0, P9 system.stackoverflowexception, P10 NIL.

可以看出来,是某个地方stack overflow了,但具体是哪里,事件查看器也没有记录堆栈信息(是不是因为太大了,无法记录?)

抓dump文件

希望出异常时能够将堆栈转储出来。原来做C++的时候,比较清楚注册表里AeDebug (HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\Current Version\AeDebug) 的设置,看了看是vsjitdebugger.exe,这是因为这台机器上最早装过vs 2005。可是这个vsjitdebugger是弹出提示的,并没有转储信息。

删除之,用drwtsn32 -i,注册了dr. watson,以前都是用这个转储的。但发现崩溃后,并没有转储东西。搜了搜,发现这个只是用来处理非托管代码的(比如C++),而托管代码需要再另一个注册表项里面: HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\DbgJITDebugLaunchSetting和DbgManagedDebugger。

看了看这两个设置: DbgJITDebugLaunchSetting = 0x10, DbgManagedDebugger = vsjitdebugger.exe。看了下进程管理器中,居然有了300多个vsjitdebugger.exe进程。把vsjitdebugger.exe进程全部杀掉,注册表里面的DbgManagedDebugger也给干掉了。

这时候,崩溃时不会再出vsjitdebugger.exe进程了。但事件管理器中会出现新的错误:

事件 ID ( 0 )的描述(在资源( .NET Runtime )中)无法找到。本地计算机可能没有必要的注册信息或消息 DLL 文件来从远程计算机显示消息。您可能可以使用 /AUXSOURCE= 标识来检索词描述;查看帮助和支持以了解详细信息。下列信息是事件的一部分: .NET Runtime version : 2.0.50727.3603 – 未找到调试器。未指定注册的 JIT 调试程序。 单击”重试”使进程处于等待状态,此时可手动附加调试器。 单击”取消”可中止 JIT 调试请求。

这个无所谓,找不到DbgManagedDebugger了嘛,把DbgJITDebugLaunchSetting 改个其它值也就行了。可是我想转储啊,这里有什么可以转储了。

主角出现:下载Debugging Tools For Windows. 安装。然后将jitdebugger设为cdb.exe (其实这里只需要托管代码的,AeDebug只是顺手),将错误转储到c:\crash_dumps下。运行下面的reg文件即可:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
“DbgManagedDebugger”=”\”c:\\debuggers\\x86\\cdb.exe\” -pv -p %ld -c \”.dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\””
“DbgJITDebugLaunchSetting”=dword:00000002
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]
“Debugger”=”\”c:\\debuggers\\x86\\cdb.exe\” -pv -p %ld -c \”.dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\”” “Auto”=”1”

如果是64位机器呢,则上面是x64目录下的cdb,再将[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework]和[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug]下面的相应项设为x86的。

好了,抓到dump文件了。

查看dump文件

用windbg来打开。记住先设置好Symbol File的目录: D:\TXWap_Work\www\TXWap\bin; c:\symbols; srv*c:\symbols*http://msdl.microsoft.com/download/symbols

前面的是自己本地的工作目录,下面有自己DLL相应的pdb。而后面的是说系统没有的话,到某个网站去下载符号文件。

然后再windbg中打开dump文件。

在windbg中要调试托管代码,需要加载一个调试器扩展SOS(Son of Strike),这个工具在Debugging Tools For Windows里面就有了。因此只要下面这样加载SOS.dll:

.loadby sos mscorwks

然后看所有的线程:

!threads

PreEmptive GC Alloc Lock
   ID OSID ThreadOBJ State GC Context Domain Count APT Exception
   20 1 3210 000dc110 1808220 Enabled 00000000:00000000 00105678 1 MTA (Threadpool Worker)
   31 2 4430 000e9fa0 b220 Enabled 04d9213c:04d93f5c 000ccbc0 0 MTA (Finalizer)
   32 3 3190 00100d48 80a220 Enabled 00000000:00000000 000ccbc0 0 MTA (Threadpool Completion Port)
   33 4 fc4 00102f00 180b220 Enabled 00000000:00000000 00105678 1 MTA (Threadpool Worker)
   34 5 15a4 00104c60 1220 Enabled 00000000:00000000 000ccbc0 0 Ukn
   35 6 30dc 001178f0 180b220 Enabled 0d303d20:0d304e60 00105678 1 MTA (Threadpool Worker)
   36 7 466c 00130a30 180b220 Disabled 0d313ac8:0d314e68 00105678 1 MTA (Threadpool Worker)
   21 8 f64 00151d08 180a220 Disabled 0335d2cc:0335da50 00105678 1 MTA (Threadpool Worker)
   39 9 3010 1b2d5070 180b220 Enabled 093004c4:0930057c 00105678 1 MTA (Threadpool Worker) System.StackOverflowException (0ecf00a4)
   41 a 28f0 1b2e2778 180b220 Disabled 11414e0c:11415924 00105678 1 MTA (Threadpool Worker)
   42 b 2174 1b2e2b70 380b220 Enabled 00000000:00000000 00105678 0 MTA (Threadpool Worker)
   43 c 35f4 1b2e3478 180b220 Enabled 11124dd8:111254d8 00105678 1 MTA (Threadpool Worker)
   44 d 1b98 1b2e40e8 180b220 Enabled 032e0e28:032e1a50 00105678 1 MTA (Threadpool Worker)
   45 e c98 1b2e4ac8 380b220 Enabled 00000000:00000000 00105678 0 MTA (Threadpool Worker)
   46 f 23e4 1b2e5608 180b220 Disabled 09300c70:0930257c 00105678 1 MTA (Threadpool Worker)
   47 10 2764 1b2e6148 380b220 Enabled 00000000:00000000 00105678 0 MTA (Threadpool Worker)
   48 11 2318 1b2e6c88 180b220 Disabled 0b3e4124:0b3e5dc8 00105678 1 MTA (Threadpool Worker)
   49 12 1cbc 1b2e77c8 380b220 Enabled 00000000:00000000 00105678 0 MTA (Threadpool Worker)
   50 13 560 1b2e8338 180b220 Enabled 0b3707ac:0b3719d0 00105678 1 MTA (Threadpool Worker)
   51 14 45c0 1b2e8ea8 180b220 Enabled 0917eacc:0918057c 00105678 1 MTA (Threadpool Worker)
   52 15 28e4 1b2e9a90 180b220 Enabled 07281228:07283190 00105678 1 MTA (Threadpool Worker)
   18 16 17a0 1b2d7fc0 180a220 Enabled 00000000:00000000 000ccbc0 0 MTA (Threadpool Worker)
   53 17 2f94 1b383e38 380b220 Enabled 00000000:00000000 00105678 0 MTA (Threadpool Worker)
   XXXX 18 9c8 1e853958 80010220 Enabled 00000000:00000000 000ccbc0 0 Ukn
   

看到了哪个线程有异常了吗?切换到39线程:

~39s

看堆栈:

!clrstack

可以看到堆栈列表。定位到是我们哪个函数出的问题,后面的事情就好办了 🙂

JavaScript引擎速度比较

自从Google的Chrom浏览器推出以来,Javascript的速度就成为大家追的焦点。一个比一个宣称快。

Mozilla的TraceMonkey: (现在Firefox3.0里面的还是SpiderMonkey)

Webkit的SquirrelFish & SquirrelFish Extreme:

于是测试了玩玩。SquirrelFish Extreme用的是Webkit r36712 nightly build版本。有空再补TraceMonkey的。

测试集 V8 IE8 SpiderMonkey SquaireFish Extreme
SunSpider 3080.8ms 12977.8ms 5744.0ms 2704.2ms
jsTimeTest 74ms 785ms(*) 329ms 107ms

(*): 这个数据可能偏大,因为中间弹出了对话框:”脚本执行时间太长,是否继续”之类的,但不比不知道,IE8还是慢得太多了。

VC 2005中的locale

连续碰到两次和locale相关的问题,一次是ifstream, 一次是boost::format, 做了些实验记录下来:

1. ifstream 的文件名参数其实可以接受char *, 也可以接受wchar_t * 作为参数,如果接受char * 的话,实际上内部也是转换成为wchar_t *,但转换的时候问题就来了,如果此文件名里面有汉字,vc2005就转换不对,这样就打不开文件了。在vc2005中,一开始程序的缺省locale是”C”, 也就是std::locale::classic()返回的,但这个locale下,汉字转换是不正确的,所以,要先用 std::locale::global(std::locale(“”))这样的语句将locale设到系统缺省的。实际上std::locale::global(std::locale(“.936”)) 也是一样的。936就是简体中文的codepage(代码页)。 此时用 locale.name() 打印出来的locale名称为: “Chinese_People’s Republic of China.936”

2. 但是一旦locale变为936了,cout 输出中文就又会有问题,碰到中文就会断掉,而且后面的东西再也显示不出来。这可以通过在ifstream打开文件之后,用std::locale::global(std::locale(“C”))再设回缺省locale来解决。

3. locale设为936的另一个问题是,boost::format 中 如果输出数字的话,1234会变成1, 234. 这个问题也可以用上面的方法来解决。另外一个方法是可以用format的第二个参数来做,例如std::locale::classic() 或者std::locale(“C”);

4. 网上说流输出时候也会将1234变为 1,234,但我直接实验cout << 1234 好像没有问题。看到说碰到这种情况,需要用a.imbue(std::locale("C"))。 5. 如果ifstream直接用wchar_t *的文件名参数,就很简单了,由于不更改locale就可以打开文件,后面的cout输出汉字也没有问题,boost::format也没有问题。所以竭力推荐这种方法 🙂

Ruby on Rails 2.0.2 中scaffold的使用

照着《Web开发敏捷之道》,写里面的depot例子,一开始就遭遇挫折:

class AdminControll < ApplicationController scaffold :product end

加了scaffold :product之后,运行提示:
undefined method ‘scaffold’ for ActionController:Class.

直接用后面的
depot>runy script/generate scaffold product admin
也不管用

上网查了查,应该是2.0.2中不再这样支持了,要用下面的语法:
depot>ruby script/generate scaffold product title:string description:text image_url:string

注意,网上有的地方是写
ruby script/generate scaffold ModelName [field:type, field:type]
但实际上不能写[], 也不能写, ,在这上面折腾了比较长时间. 最后还是通过直接运行ruby script/generate scaffold看它的帮助和示例才明白过来。

这样生成的001_create_products.rb里面也自动定义了相关的字段,不用手工添加了。自动生成的和书上例子里面也稍有不同,书中例子是:

class CreateProducts < ActiveRecord::Migration def self.up create_table :products do |t| t.column :title, :string t.column :description, :text t.column :image_url, :string t.timestamps end end def self.down drop_table :products end end

自动生成的是:

class CreateProducts < ActiveRecord::Migration def self.up create_table :products do |t| t.string :title t.text :description t.string :image_url t.timestamps end end def self.down drop_table :products end end

第一个ruby程序

现在手头的项目隔三岔五地要往用户的服务器上传当前版本,总是要打开FTP客户端来传。应该自动化这个工作的,正好学习ruby中,就写了个ruby脚本,可以按照当前时间自动传。


require ‘net/ftp’

date = $*[0];
if date == nil
date = Time.now.strftime(‘%y%m%d’)
end

ftp = Net::FTP.new(‘ftp.examples.com’)
ftp.login(‘username’, ‘password’)
ftp.chdir(‘download’)

filename = ‘history.htm’
puts “sending #{filename}”
ftp.putbinaryfile(filename, filename, 1024)

filename = ‘shocs’ + date + ‘.rar’
puts “sending #{filename}”
ftp.putbinaryfile(filename, filename, 1024)

ftp.close

继续mysql中的charset

继续折腾mysql的charset。又发现数据库安装到客户的机器上之后,某几个表INSERT一行数据的时候存入的汉字都是’?’,但UPDATE的时候又可以修改为正确的汉字。差别是,这几张表的INSERT数据是用存储过程做的,但UPATE是直接在PHP语句做的,而且PHP里面使用了”SET NAMES ‘utf8′”. 基本认为是存储过程的问题。网上查了资料,http://unix-cd.com/vc/www/26/2007-06/2948.html提到了:

由于存储过程中定义参数时,无法定义其字符集,因此调用存储过程的时候,会默认读取全局变量character_set_server,而且还是只读取mysqld启动时该全局变量的值作为存储过程中默认的传输字符集。因此,如果数据表/字段使用系统默认的字符集(比如utf8)的话,调用存储过程更新一些非英文的字符串字段时,就不会发生问题;但是,如果数据表/字段的字符集不是系统默认的字符集(比如默认是utf8,数据表使用的是 utf8),就会出现问题了。解决办法是:在my.cnf(windows下是my.ini),增加一行:

default-character-set=utf8

或者

character-set-server=utf8

也可以。如果只是在mysqld启动之后,在 mysql 命令行中用 SET 语法来更新的话则不起作用。

看到,客户机器上他们自己安装的MySQL服务器缺省字符集是utf8,因此问题基本明确。但加入default-character-set=utf8后,仍然不起作用。又做了一些试验的结果发现,新建一个数据库,同样的存储过程就可以了。最后发现虽然数据表设置了’utf8’的字符集,但原来创建数据库的时候,由于没有指定字符集,因此使用了默认的字符集,也是utf8了。好象存储过程并不是完全跟着character_set_server走,而是跟着所属数据库的字符集走。把数据导出,再导入新建的数据库,问题解决。

没有测试过,当character_set_server为utf8,而数据库建的时候用了utf8的话,还会不会出这个问题,测试一下,就应该知道到底存储过程到底受谁的影响,或者是两者都受。

当数据库是utf8的时候,为什么UPDATE后汉字是正确的呢?我猜测可能的解释:
1. 对于一般SQL语句:UTF8的数据来到服务器之后,因为表设置为UTF8,因此会存储utf8的内容,没有问题。
2. 对于存储过程:UTF8的数据来到服务器之后,因为数据库设置为utf8, 因此会做一次转换,转成utf8的数据,进入存储过程,然后实际插入的时候,由于表为UTF8的,那么再转换成UTF8的,然后插入。汉字在转换为utf8的这一步变成了’?’。

mysql中的charset

今天碰到一个问题,用C++写了一个访问mysql的程序,在我自己机器上好好的,到别人的机器上就不对了,汉字都显示成??。底层使用了mysql++,它再用了mysql的C API接口。

开始查询资料:

1. 首先是mysql++里面关于字符集的说明,里面提到了关于Unicode不直接支持,是通过UTF8来支持的。但我的程序是MBCS的,没有用Unicode。没啥关系。

2. 网上查询mysql的字符集的资料,提到了一些关于SET NAMES的描述. 按照里面的说法,进mysql命令行的客户端,show variables like ‘character_set_%’, 两台机器上都是 character_set_client, character_set_connection,character_set_results几项都是UTF8. 但这就不对了,我程序用的是MBCS(GBK), 并不是UTF8,为啥在我的机器上还能显示对呢?另外,即便是UTF8,显示的也不应该是‘?’啊。

3. 比了比两台机器MySQL Server目录下的my.ini文件, [mysql]下写的都是default-character-set=utf8, 这倒是和mysql命令行客户端的表现是一致的。

4. 我试着在我的程序里加上了SET NAMES ‘gbk’的调用,居然两台机器上都对了。

5. 可是还是不清楚为什么会两台机器不一样。于是在我的程序里SET NAMES ‘gbk’的前后都加上show variables show variables like ‘character_set_%’, 发现不一样的地方了。在我的机器上,一开始character_set_client, character_set_connection,character_set_results就都是GBK,所以SET NAMES ‘gbk’实际上也不起作用。而别的机器上,一开始这几项都是’utf8’,设完后变成了’gbk’,这下说得过去了。如果这几项是gbk的话,我读出来的直接都是gbk字符,显示也正确了。而如果没有SET NAMES,别的机器上开始是’utf8’,这下汉字都转成’?’,这是有道理的。

6. 虽然现在问题解决了,但还需要追下去。现在的问题就变成了为啥我的机器开始的时候是GBK,而别人的机器开始是utf8? 而mysql客户端进去可能是因为读了my.ini后主动设置成utf8的,这是正常的。

7. 没辙,跟踪进mysql++,发现在调用mysqlpp::Connection::connect() 里面,调用C API的mysql_real_connect之后,mysql句柄的codeset项就被改变了。

8. 还是没辙,试试在connect之前用mysqlpp::Connection::set_option() 把opt_set_charset_name 设成latin2,发现别人的机器设完那几个’character_set-%’变成latin2了,但我的机器上仍然始终是gbk.

9. 以前下载过mysql的源代码,找到mysql_real_connect, 发现一开始就用到了mysql->options.my_cnf_file,如果设置了,会用它重新读取options,觉得看到解决的曙光了。试试在connect之前用mysqlpp::Connection::set_option() 把opt_read_default_file设成一个不存在的”haha”,这下我的机器也是utf8了。

10. 再看mysqlpp::Connection里面,有一句set_option_default(opt_read_default_file, “my”); 因此,缺省情况下(不设置opt_read_default_file的时候)两台机器上实际上都会去用my.ini初始化。可是我比较的my.ini一样啊。

11. 简单浏览mysql源代码,找到init_default_directories,前面的注释说
On Microsoft Windows, this is:
1. C:/
2. GetWindowsDirectory()
3. GetSystemWindowsDirectory()
4. getenv(DEFAULT_HOME_ENV)
5. Directory above where the executable is located
6. “”
7. –sysconfdir=

于是查找这些目录下,有没有my.ini,发现我的windows目录下有一个my.ini, 打开一看,哈哈,[client]里面有 default_character_set = gbk. 把这个文件删掉,ok了,我的机器上也是utf8了。

12. 进一步试验,发现MySQL Server目录下的my.ini对于mysql++或mysql C API没有任何用处,也是,这个目录不在上面列出来的7项之内。

13. 这下明白了,实际上在我的机器上,这里找到的是windows目录下的my.ini,因此设置了client_character_%为gbk,误打误撞我的机器上显示就对了。而对于别人的机器,没有合适的my.ini, 因此缺省为utf8. 而mysql总是找MySQL Server目录下面的my.ini里面的[mysql]下面的。

14. windows目录下的my.ini可能是原来装过4.x版本的mysql留下来的。

微软架构师谈编程语言发展

http://blog.csdn.net/hellothere/archive/2007/07/29/1715993.aspx
不错的文章. Herb一如既往地关注”并行处理”, Anders提到”命令式”,”函数式”,”声明式”语言. 方向:将函数式语言特性引入到命令式语言中来,如现在LinQ所做的。抽象的范围和抽象的层次之不同,不是要上移抽象层次,而是要增加抽象层次。微软的F#被认为是一种很好的语言。

R6025 – pure virtual function call

今天的一个程序出了 R6025 – pure virtual function call 错误,主要原因是在基类的构造函数中调用了纯虚函数。
1. 如果不是纯虚函数,没问题。
2. 如果构造函数直接调用纯虚函数,链接时会出错。只有通过一个其它成员函数转调一下。

下面是一个简化的例子:
class CBase
{
public:
CBase() { func2(); }
virtual void func() = 0;
void func2()
{
func();
}
};

class CDrived : public CBase
{
public:
CDrived() { }
virtual void func() { printf(“hello”); }
};

int _tmain(int argc, _TCHAR* argv[])
{
CDrived * d = new CDrived();

return 0;
}

typeid

class Base
{
public:
void vvfunc() {}
};

class Derived : public Base
{

};

int _tmain(int argc, _TCHAR* argv[])
{
Derived* pd = new Derived;
Base* pb = pd;
cout << typeid( pb ).name() << endl; //prints "class Base *" cout << typeid( pd ).name() << endl; //prints "class Derived *" try { // Base * pp = NULL; // cout << typeid( *pp ).name() << endl; cout << typeid( *pb ).name() << endl; //prints "class Derived" cout << typeid( *pd ).name() << endl; //prints "class Derived" } catch (std::__non_rtti_object e) { cout << "__non_rtti_object" << endl; } catch (std::bad_typeid e) { cout << "bad_typeid exception" << endl; } delete pd; return 0; }

Visual C++ .NET下,如果/GR 未打开,这个程序仍然成功打印.

但如果 public: virtual void vvfunc(), 则 typeid( *pb ), typeid( *pd )的会错误,抛出__non_rtti_object异常。

如果上面注释的两句打开,则会跑出__bad_typeid异常。

__non_rtti_object继承自__bad_typeid.