银行记账接口(续)

记账接口做什么?

1. 公共校验
这个没什么好说的,就是对上层应用传入的合约号,会计日期等参数以及是否允许透支等标志的合法校验,值得一提的是,合约的币种和交易币种需要保持一致,人民币账户不能记美元。结售汇转换应该放在应用来做。

2. 更新合约当前余额
最基本的功能,也没什么好说的。

3. 根据账户余额类型及发生额生成对应的合约分录

4. 根据账户余额类型、发生额及分录关系码生成合约分录
意思是,外围调用记账接口后,会自动触发一些账户余额,特别是统计类余额的变化。这些统计类余额本质上是由于合约余额动账产生的,比如某合约取现后,会联动触发累计取现金额这个余额类型产生变化。所以为了减少应用调用记账次数,会事先配置好分录关系码,记账时根据分录关系码生成多条分录。(对于理财应用,比如某贷方金额动账后,记账接口还需要对份额进行加减。)

5. 联动生成合约明细并自动开户
比方说A行部向B行部转账1000.00元,对于A行部,钱少了1000元,对于B行部,钱多了1000元。AB行部之间需要补一对特殊的合约(通存通兑),来保证A行部和B行部本身的平衡。再复杂一点,A行部是新成立的行部,发生交易时这个特殊合约还没开立,这时候记账接口需要进行开户操作。

6. 登记待更新日终余额表
对于采用日终余额法计息的合约,如果有动账则登记这张表,当晚日终余额加工批量程序会从这张表出发,对登记余额类型进行更新。这个日终余额表只起到了框定范围的作用。
补充:对公存款的个人活期支票户采用积数法计息,其他都采用日终余额法计息。

7. 人民币账户记外币
只有一个场景,即外币收手续费,某个跨国公司在我国内开立了分公司,并开立人民币账户。缴纳手续费时可能会转入外币。这个时候合约余额没法使用原有的合约,因为合约余额类型是没有币种信息的,默认都和原合约相同,人民币合约下所有的合约余额都是人民币的。这个场景下发生业务的合约是一个客户合约,并不是为了收取手续费的专用合约,ABC采用了一个新的数据模型,合约外币余额。

8. 入上日账
这个稍微复杂一点,入上日账只允许在批量模式下出现,通过记账掩码入上日账处理标识判断是否为入上日账处理(只允许在批量交易下入上日账),如果是,则获取上一日的会计日期,生成明细和更新余额时都按照上日会计日期进行判断处理。

  • 将合约明细上的入上日账处理标识赋值为‘1’,明细中的会计日期赋值为上日会计日期
  • 根据明细,更新当前合约余额
    • 明细中的会计日期 = 更新前余额表中的最后动账日期,说明切日之后没有发生当日动账交易。(与入当日账时的处理规则相同)更新最后动账日为明细中的会计日期,更新当前余额,次末动账日和次末动账日余额不变。(最简单的情况)
    • 明细中的会计日期 > 更新前余额表中的最后动账日期,此时说明入上日账前到上一日的日切(即整上一会计日),都没有发生动账交易。(因为如果但凡有一次动账,最后动账日期即被修改为上一会计日)更新次末动账日为最后动账日,更新次末动账日余额为当前余额,更新最后动账日期为明细中的会计日期,更新当前余额。(入上日账变成了上日第一次动账,与入当日账时的处理规则相同)
    • 明细中的会计日期 < 更新前余额表中的最后动账日期,说明入上日账前(切日之后)已经发生了当日动账交易,比如说半夜十二点多你做了一笔支付,余额表中的最后动账日期已经更新为当前会计日期,并且次末动账日期已经更新为上日,次末动账日余额已经更新为上日的日终余额。最后动账日不变,更新当前余额,更新次末动账日余额(与当前余额同加同减,处理逻辑类似冲账,当不允许透支时,更新后的次末动账日余额不能小于零),如果明细中的会计日期>次末动账日期,则需要将次末动账日期修改为明细中的会计日期(入上日账时的特殊处理规则)。

多说一句,日切通常是在晚上12点整开始,12点后开始批量处理上日账务,银行日切的时间通常很短,而遇到系统维护或主机升级,为了保证记账正确,日切通常会提前。比如说你在11:59分购买了某行的某理财产品,碰巧这天这个银行系统升级,日切提前到了11:58,这相当于你购买当天的利息被银行扣掉了。

9. 汇总入账
记账接口判断入账模式:实时更新,批量汇总更新;账户余额类型的属性是实时更新的,无视传入的状态位,否则以传入的为准。如果是批量汇总更新(即日终批量进行汇总入账),生成的分录上的更新处理状态为“批量更新”,不修改余额。对于主机系统造成压力比较大的交易一般会考虑以汇总入账的方式记账。

10. 抹账
获取需要抹掉交易的日志号,新生成的交易合约明细和原正交易的信息一样,只是新生成的合约明细账务处理状态为抹账,完成平衡校验后,根据日志号修改原合约明细账务处理状态为被抹,登记交易间关系表为“抹账关系”。
特殊情况是,抹账交易可能会触发其他正交易。目前只有两种场景:

  • 某对公账户,当余额大于100万时会自动触发资金归集,抹最后一笔余额大于100万的交易,被触发资金归集的交易不能抹。
  • 个人活期联机结息,正常情况下,个人结息交易是在批量程序处理的,但是如果批量结息失败,在下次联机动账时会检查结息时间,发现上次没结息就自动触发个人联机结息。这个时候如果要抹账的话,联机结息操作不能抹。

对于如上两种特殊情况,需要应用对已经发生的分录做部分抹账,做法是送记账接口的记账掩码为正常处理,然后根据输入的分录按照正交易逻辑进行余额更新处理。也就是说,特殊抹账是按照正交易来做的。功能很灵活,当然,也容易出错。

11. 冲正(错账调整)
要冲的错账应该小于当前会计日期,目前记账不支持记未来账(但也不是不能做,无非就是倒起息和预起息),如果合约是采用积数法计息,则计算调整积数并更新(复杂);如果是余额法计息,则根据规则计算日终余额调整金额并更新日终余额调整表,结息由专门的结息程序来计算。

  1. 余额法计息,以当前会计日期和错账日期为区间,查询日终余额调整表,如果存在记录(说明当天已经调整过至少一次了),则累加记录中的调整金额,如果查询不到记录,则新增一条记录(当晚批量程序会找余额调整表和余额表,计算利息,这样设计使得日终余额表和余额调整表分别记录余额和需要调整的金额)。
  2. 积数法计息,贷方余额变动,比较生效日期和滚积数日期
    1. 生效日期(T1)大于上次滚积数日期(T2,即为合约余额表里积数的最后动账日期),则正常滚积数,积数发生额 = 当前贷方余额×(T1-T2)
    2. 生效日期(T1)等于上次滚积数日期(T2),不处理
    3. 生效日期(T1)小于上次滚积数日期(T2),则计算并更新调整积数,调整积数=合约余额变动金额×(T2-T1)

银行通过两种计息法保证用户的利息结算准确,一个是积数法计息,一个是余额法计息。积数法对于银行开销较小,余额法开销较大。积数法记录上次动账日到当前会计日的积数,结息后会清零;余额法会根据当前积数计算余额利息并记录余额调整表。对于活期而言,为了减小开销可以使用积数计息法,对于对公账户,为了提高精确性使用余额计息法。当然,两种结息算法得出的利息是完全一样的,只是计算成本不同。另外,计息账户发生动账时会触发动账结息。比如你有10000块钱,存了两天,虽然银行可能还没来得及结息,但是如果你想销户的话银行还是会自动为你按照活期利率给你这两天的利息。结息后本金和计提的利息一并记入本金。举个例子说明:
2016年6月1日,你在A银行存5万元一年定期,年利率2.5%,31天后取出1000元,此时取出的1000元会按照活期利率计算利息,剩余的49000元仍然按照定期计算利息。

关于定期/活期存款的结息日:活期的利息并非每天都结,央行规定按季度结息,而且不同银行有自己的结息日。

  1. 计算天数:即该余额保持不变的天数
  2. 计算积数:本金×时间
  3. 计算结息:积数×日利率

50000*32*0.025/360 = 111.11,算得5W元一个月的定期应付利息会从111.11变成107.64,假设A银行结息日为7月2日,正常情况下,结息后应付利息调整为107.64 + (50000-1000)*0.025/360 = 111.04。这个金额会累加到本金里(记贷方余额,不知道是什么东西的同学请翻上一篇)。如果这时发生冲账,要冲掉取现1000的交易,记账接口记日终余额调整表,贷方+1000。少记的利息会在下个季度的计息程序补足。如果这个时候销户的话也会进行结息。

12. 特殊动账,个人活期批量结息时,只更新最后动账日,不更新客户最后动账日。比如中石化(大客户)的对公账户,有自己的会计系统,如果账户未动账的时候我们发起了一笔结息(假设每个季度23号),然后更新了最后动账日期,中石化的财务肯定不会满意。

13. 借贷平衡校验
从明细全局内存中读取联机交易写入的所有合约明细数据,按照行部、币种将借方发生额和贷方发生额分别累加汇总后,判断总借方金额与总贷方金额是否相等,如不等,则报错。实际上,财会部为每组交易配置了会计模板(核算操作码),因此在一个交易下(核算场景下),还要对发生交易的多个行部发生的交易信息进行平衡校验。

银行记账接口

银行的记账和处理是个很复杂的过程,可以说从业务逻辑到技术实现都是很有技术含量的。2016年双十一农业银行(以下称ABC)全天交易量超过4亿笔,10号晚上只有一个人值班(啥事也没干)。据我所知没有任何一家互联网公司能做到。当然了,各家互联网公司的业务复杂程度和技术架构,解决方案与传统银行完全不一样,业务需求也不同,各家各有特点。

言归正传,在介绍记账之前,需要同学们先了解一些会计的基本概念。考虑到银行核心系统内部会有部分涉密的东西,我会尽量过滤掉这部分内容,只做一般化的讨论。

银行系统在处理上分为联机交易和批量交易两大部分,从字面意思上可以知道,联机就是实时处理,实时更新,比如活期账户取现。批量则是对当天处理的结果或者待入账信息等临时表进行汇总处理,具体包括清算、轧差等。记账接口在技术上是整个核心系统数据的来源,各个子应用(存款,贷款,理财等)通过记账接口完成对合约明细的写入。一般当天晚上,核心系统会从明细表出发进行一系列复杂的处理。换句话说,这个接口主要和合约明细这个表打交道。这个合约和我们所说的账户不一样,通常意义上的账户,比如说活期账户,是银行记账的维度,而为了明晰账簿,ABC采用的是以合约的形式记账,活期合约下,有活期合约余额类型和利息合约余额类型,通过合约的形式来逐户记账的方式叫做分户账。”账户”被分的更细了,这么做的意义在于,除了分户记账,我们还可以采取其他的维度去记账,比如按照一定的统计维度(网点、币种、产品、账户余额类型)来分门别类的统计。(分类账
举例说明:
某银行托管的基金合约下,客户持有的份额(余额类型一)是10000.00元
客户购买的暂存资金(余额类型二)是30000.00元
异常待处理应付客户资金(余额类型三)0.00元
同一个合约下不同的余额类型需要有各自不同的动账时间等属性。分类账用于账务核对,满足对可售产品(比如某定期,对于银行来讲就是一个可以卖给客户的产品)进行汇总的业务需求。上合约中,银行可以对客户购买的暂存资金进行冻结,交易金额封顶操作,等。
定义如下:
分户账:采用合约的形式来逐户设置,反应金融性交易的经济事项变动即时余额信息。
分类账:也称汇总账簿,按不同的维度分门别类的反应分户账汇总信息。
实际上,ABC还需要保留一个账簿叫做明细账,按照业务发生的时间顺序,逐日逐笔记录业务的账簿,这个保留的是最最原始的凭证信息。这个账簿以业务场景为出发点。

基本概念相关

合约:可以理解为客户和银行之间的一种契约。详细解释可以参考这里
合约分录
:合约分录是合约的资金或其他信息产生变化时产生的一条明细记录,按照是否有资金变动,合约分录可以分为金融性合约分录和非金融性合约分录,通过记账接口记录的均为金融性合约分录,即某账户余额类型的金额变动明细。也是我们记录的描述合约的最小单位。
合约明细:用来反映某一笔交易对某一个合约的影响,即当前合约的业务场景。一笔合约明细由一笔交易产生的、属于某一合约的全部合约分录组成。通过记账接口记录的都是金融性合约明细。即一笔交易中某一个合约下发生金额变动的账户余额类型的明细记录。
合约余额:反映一个合约下的某一账户余额类型的当前余额信息。
借贷平衡校验:按照借贷记账法,对一笔联机交易生成的所有合约明细数据,按照行部、币种将借方发生额和贷方发生额分别累加汇总后,利用“有借必有贷,借贷必相等”的记账规则来检验本交易记账是否正确。
抹账与冲正:对用户操作失误引起的错账进行调整,错账分为当日错账、隔日错账两种。对当日发生的账务差错,经高级主管审批,由原经办用户选择抹账交易,进行抹账处理。对非当日的其他账务差错,做冲正处理。多说一句,抹账和冲正都是对错误的补救。抹账相当于把原来发生的交易抹除,冲正相当于把原来发生的错误账务进行弥补,比如某天交易柜员需要转出100.00元到现金箱,但手抖多打了个0,转出了1000.00。第二天柜员发现前一天做错了,这个时候柜员可以采取两种措施:一种是从现金箱往回转9000.00,一种是把10000.00全部冲回去,重新转1000.00出来。往原交易方向的反向记叫冲正,记红字,正向冲叫补账,记蓝字。如果是涉及计息的合约,还需要对算错的利息进行调整(你也不希望银行平白无故多吃你的利息吧),这个待会再讨论。

假设储户以现金形式存入个人活期储蓄存款1000.00元,对于个人来说,钱(现金)减少了1000元,但是卡里的钱多了1000元,看起来平了。对于银行来说,钱存到了现金箱,然后进入了核心系统,看起来好像现金箱里多了1000元,然后核心系统的个人活期合约里也多了1000元,总共多了两千,好像哪里不对。 ^——^

实际上,会计上有这么一个公式:
资产=负债+所有者权益
任何交易发生,都需要根据“有必有借贷必相等”的记账规则对交易进行借贷平衡校验。这里的借和贷和我们通常意义上借给朋友十块钱的借不同,会计上的借贷表示记账符号,借表示账户的借方,贷表示账户的贷方。对于资产来说,借表示资产的增加(+),贷表示资产的减少(-)。对于负债或所有者权益来说,借表示负债、所有者权益、利润减少(-),贷表示负债、所有者权益、利润增加(+)。
什么意思呢?根据表示意义的不同,银行把合约号(会计上叫科目号)分为资产类、负债类、所有着权益类等,不同类的科目账户增加或减少对于银行来讲意义是不一样的。比如说,个人的活期账户,对于银行来讲属于负债类科目,因为这是银行欠客户的,而固定资产,短期贷款等,这是银行自有的或者别人欠银行的,属于资产类科目。一个金融性交易,必然会导致至少两个账户的动账,在处理业务和会计事项时,必须同时登记借方科目和贷方科目,不管一借多贷还是多借一贷甚至多借多贷,借贷双方的金额必须相等。这就是“有借必有贷,借贷必相等”。
那资产=负债+所有者权益呢?
刚才说了,个人的活期存款在银行是负债,所以记录的是负债类科目,而库存现金是银行的资产,所以上述提到的业务场景应记的会计分录为:
借:库存现金 1000.00元                           (资产)
贷:个人活期储蓄存款  1000.00元        (负债)
这样就平了。再举几个例子:
银行给企业发放短期贷款一笔,金额为1000000.00元,转入该企业存款账户:
借:短期贷款  1000000.00元                   (资产)
贷:单位活期存款  1000000.00元         (负债)
银行向中国人民银行解交回笼资金100000.00元
借:存放中央银行款项  100000.00元     (负债)
贷:库存现金  100000.00元                   (资产)
再说借贷,一般来说,一个账户的左方称为借方,右方称为贷方,在哪方登记增加数,在哪方登记减少数取决于账户本身的性质。资产与负债和所有者权益是同一个资金的两个对立面,他们之间应该保持这平衡关系,因此,这两类账户必须以相反的方向来登记其金额的减少和增加。

通常:资产类账户借方登记增加数,贷方登记减少数;负债类和所有者权益类贷方登记增加数,借方登记减少数。因此对于资产类账户,余额增加记入借方,对于负债类科目和所有者权益类科目,余额增加记贷方
对于资产类账户的余额和发生额关系表示为:
期末余额  =  期初余额  +  借方本期发生额  –  贷方本期发生额
(借方)      (借方)
负债类和所有者权益类账户余额和发生额关系表示为:
期末余额  =  期初余额  +  贷方本期发生额  –  借方本期发生额
(贷方)      (贷方)
对如上例子,发生额及余额试算如下:

账户名称 期初余额 本期发生额 期末余额
借方 贷方 借方 贷方 借方 贷方
库存现金 1000000 1000 100000 90100
个人活期存款 1000 1000
企业短期贷款 1000000 1000000
单位活期存款 1000000 1000000
存放中央银行款项 100000 100000

未完待续。

perf patch related –每每看到xiao桑的代码都惊为天人

+typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int);
+struct kvm_event_key {
+	const char *name;
+	key_cmp_fun key;
+};
+
+static int trace_vcpu = -1;
+#define GET_EVENT_KEY(func, field)					\
+static u64 get_event_ ##func(struct kvm_event *event, int vcpu)		\
+{									\
+	if (vcpu == -1)							\
+		return event->total.field;				\
+									\
+	if (vcpu >= event->max_vcpu)					\
+		return 0;						\
+									\
+	return event->vcpu[vcpu].field;					\
+}
+
+#define COMPARE_EVENT_KEY(func, field)					\
+GET_EVENT_KEY(func, field)						\
+static int compare_kvm_event_ ## func(struct kvm_event *one,		\
+					struct kvm_event *two, int vcpu)\
+{									\
+	return get_event_ ##func(one, vcpu) >				\
+				get_event_ ##func(two, vcpu);		\
+}
+
+GET_EVENT_KEY(time, time);
+COMPARE_EVENT_KEY(count, stats.n);
+COMPARE_EVENT_KEY(mean, stats.mean);
+
+#define DEF_SORT_NAME_KEY(name, compare_key)				\
+	{ #name, compare_kvm_event_ ## compare_key }
+
+static struct kvm_event_key keys[] = {
+	DEF_SORT_NAME_KEY(sample, count),
+	DEF_SORT_NAME_KEY(time, mean),
+	{ NULL, NULL }
+};

回头分析

C programming language calling procedure with assembly language description

Source code:
main.c

int g(int x)
{
return x + 3;
}

int f(int x)
{
return g(x);
}

int main(void)
{
return f(8) + 1;
}

The assembly language is generated by:
gcc –S –o main.s main.c -m32
Assembly Source Code

 

 

 

 

 

 

 

 

 

 

_main:                               ## @main
pushl %ebp                       ##  suppose ebp = esp = 2000
movl %esp, %ebp
subl $4, %esp
movl $20, (%esp)              ## set content in %esp to 20 for later use. (Parameter)
call f: push %eip; movl %(f), %eip
## suppose f is 23, then 23 stores in addresss 1992
## see the sketch below for more details
_f:
pushl %ebp                       ## save ebp and esp = esp – 4
movel %esp, %ebp           ## let the new ebp points to the new esp
subl $4, %esp
movl 8(%ebp), %eax         ## eax = (%ebp) + 8 = 20
movl %eax, (%esp)           ## (%esp) = 20
call g: push %eip, movl %g, %eip
_g:
to be contined by readers. 🙂

Source Code Analysis

Thanks for reading!

perf tool source code analysis: perf record

Perf tool in Linux Kernel is used to analyze various kinds of performance issues. More information could be accessed here, but this article goes with the typical calling procedure of the built in core function “cmd_record”. Source code of perf could be found in linux/tools/perf/perf.c. Now let’s begin with function main(). You could read this article for a quick review. Don’t panic!
Take perf record -a sleep 3 for example.
Initialization:
1. main()->run_argv()->handle_internal_command()->run_builtin()->
status = p->fn(argc, argv, prefix)->cmd_record()
build a new record struct rec with struct record *rec = &record;
/*record is initialized with the following code*/
static struct record record = {
    .opts = {
        …
    },
    .tool = {
        …
    },
};
Skip these data structures as you wish but don’t hesitate to look up for the variables later.
struct perf_evlist {
    struct list_head entries;
    struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];//1<<8
    struct fdarray     pollfd;
    struct thread_map *threads;
    struct cpu_map      *cpus;
    struct perf_evsel *selected;

};
Note: perf_evsel stands for one event, and perf_evlist stands for all events we selected, which perf uses to communicate with kernel.
2. rec->evlist = perf_evlist__new();
struct perf_evlist *evlist = zalloc(sizeof(*evlist));
perf_evlist__init(evlist, NULL, NULL);
2.1 init all 256 struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]  to NULL
2.2 init struct list_head entries
2.3 perf_evlist__set_maps(evlist, NULL, NULL)
  • set evlist->cpu and evlist->threads to NULL
  • perf_evlist__propagate_maps
    1. struct perf_evsel *evsel;
    2. evlist__for_each(evlist, evsel) {
      set all evsel->cpu and evsel->thread in evlist to NULL;
      }
      # __evlist__for_each(&(evlist)->entries, evsel)
      # list_for_each_entry(evsel, &(evlist)->entries, node)
      # struct list_head entries; entries belongs to struct evlist
      # struct list_head node; node belongs to struct evsel,
      # we use node to insert evsel into evlist->entries or in other list_heads
      # #define list_for_each_entry(evsel, list, node)
      # for (evsel = list_first_entry(list, typeof(*evsel), node);      
                   &evsel->node != (list);                    
                   evsel = list_next_entry;
      # evlist and evsel are connected through double linked list struct list_head entries in evlist and node in evsel. We’ll talk about it later.
  • fdarray__init(&evlist->pollfd, 64);
    # set fdarray.nr_autogrow to 64 and others to 0 or NULL;

3. perf_evlist__add_default(rec->evlist);
    struct perf_event_attr attr = {
        .type = PERF_TYPE_HARDWARE,
        .config = PERF_COUNT_HW_CPU_CYCLES,
    };
add a new evsel named “cycles” to evlist
3.1 evsel = perf_evsel__new(&attr);
3.2 perf_evlist__add(evlist, evsel)

  • entry->evlist = evlist; # note: put evsel in one evlist for further useage
  • list_add_tail(&entry->node, &evlist->entries);
    # note: list_head is a double linked list which is quite commonly used in kernel.
4. target__parse_uid(&rec->opts.target)
target->uid = UINT_MAX;
5. perf_evlist__create_maps(rec->evlist, &rec->opts.target)
target = {pid = 0x0, tid = 0x0, cpu_list = 0x0, uid_str = 0x0, uid = 4294967295, system_wide = true, uses_mmap = true, default_per_cpu = true, per_thread = false}
5.1 evlist->threads = thread_map__new_str(target->pid, target->tid, target->uid)
  • thread_map__new_str(NULL, NULL, UINT_MAX)
  • thread_map__new_by_tid_str(NULL);
  • thread_map__new_dummy();
  • thread_map__alloc(1);
  • thread_map__realloc(NULL, __nr);

# allocate sizeof(struct thread_map) + sizeof(struct thread_map_data) for thread_map
5.2 cpu_map__new(target->cpu_list);

  • cpu_map__read_all_cpu_map()
    1. fp = open(/sys/devices/system/cpu/online, r)
    2. struct cpu_map *cpus = cpu_map__read(fp);
      # read online cpus and allocate a integer array for them. (tmp_cpus[0,1,2,3,…])
    3. cpu_map__trim_new(nr_cpus, tmp_cpus)
      struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)*nr_cpus);
      memcpy(cpus->map, tmp_cpus, sizeof(int)*nr_cpus);
      # copy the content of online cpus directly to cpus->map.

    Till now, target->cpu_list got online cpu information.
    Like: for (i = 0; i < nr_cpus; ++i) cpus->map[i] = i;

5.3 perf_evlist__propagate_maps(evlist, !!target->cpu_list)
ensure each evsel in evlist get the right cpu_list
5.4 record_opts__config(&rec->opts)
set recording frequency for perf
CORE FUNCTION __cmd_record(&record, argc, argv)
struct machine {
    char          *root_dir;
    struct dsos      dsos;
    struct map_groups kmaps;
    struct map      *vmlinux_maps[MAP__NR_TYPES];
    u64          kernel_start;
    symbol_filter_t      symbol_filter;
    …
};
struct map_groups {
    struct maps     maps[MAP__NR_TYPES];
    struct machine     *machine;
atomic_t     refcnt;
};
1. perf_session__new(struct perf_data_file *file, bool repipe, struct perf_tool *tool)
Will be called by perf record with write way.(perf report with read way)
perf_session__new(&rec->file, false, &rec->tool);
1.1 machines__init(&session->machines);
machine__init((struct machine)&machines->host, “”, HOST_KERNEL_ID);
  • map_groups__init((struct map_groups)&machine->kmaps, machine);
    maps__init((struct maps)&mg->maps[i]);
  • dsos__init(&machine->dsos)

1.2 ordered_events__init(&session->ordered_events, ordered_events__deliver_event);
init ordered_events
1.3 perf_data_file__open(file)
by default open the output file as “perf.data” with create mode
1.4 perf_session__create_kernel_maps(session)
machine__create_kernel_maps(&session->machines.host)
allocate memory for the initialized session->machines.machine

  • struct dso *kernel = machine__get_kernel(machine);
    • kernel = machine__findnew_kernel(machine, vmlinux_name, “[kernel]”, DSO_TYPE_KERNEL);
      machine__findnew_kernel(machine, “[kernel.kallsyms]”, “[kernel]”, 1);
      struct dso *dso = machine__findnew_dso(machine, “[kernel.kallsyms]”);
      dsos__findnew((struct dsos)&machine->dsos, “[kernel.kallsyms]”)
      find kernel with name “[kernel.kallsyms]”

      dso__get(__dsos__findnew(dsos, name));->
      __dsos__findnew(dsos, name)->
      __dsos__find(dsos, name, false);->

      __dso__find_by_longname(&dsos->root, name);->
      Make a dynamic shared object with “[kernel.kallsyms]” then insert it into &machine->dsos
      __dsos__addnew(dsos, name);->
      dso__new(name);->
      __dsos__add(dsos, dso);

    • dso__read_running_kernel_build_id(kernel, machine);
      Read build_id from /sys/kernel/notes which n_type=3 and n_namesz=3
      sysfs__read_build_id(path, dso->build_id, sizeof(dso->build_id)
      read_build_id(void *buf, buf_size,dso->build_id, sizeof(dso->build_id), false); // buf_size (stbuf.st_size of /sys/kernel/notes) typical value: 360B
      //Symbol-minimal.c
      // File note is made of series of structure like
      struct {
           u32 n_namesz;
           u32 n_descsz;
           u32 n_type;
      } *nhdr;
      if (nhdr->n_type == NT_GNU_BUILD_ID &&
      nhdr->n_namesz == sizeof(“GNU”))
      In the first n_namesz stores a pointer points to the name of the very field. See the code here for the whole content of notes file.
      Copy the very desc to dso->build_id.
      Set dso->has_build_id = true.
  • machine__get_running_kernel_start(machine, &name);
    Figure out the start address of _text or _stext in /proc/kallsyms
    addr = kallsyms__get_function_start(filename, name); //filename = “/proc/kallsyms”
    kallsyms__parse(kallsyms_filename, &args, find_symbol_cb)
    Content of /proc/kallsyms filled with lines like 00000000 t fuse_async_req_send
    Read each line and find the very line c1000000 T _text | _stext and record the start address probably c1000000 to start address then return the hex value.(3238002688)
  • __machine__create_kernel_maps(machine, kernel) < 0)
    start = machine__get_running_kernel_start(machine, NULL);// do it again
    Create struct map *map for all MAP__NR_TYPES for passed in structure machine and allocate sizeof(struct map) + sizeof(struct kmap) for each of them.
    for type from 0 to MAP__NR_TYPES
    machine->vmlinux_maps[type] = map__new2(start, kernel, type);
    map__new2(start, kernel, type);->
    map__init(map, type, start, 0, 0, dso);
    void map__init(struct map *map, enum map_type type, u64 start, u64 end, u64,
    pgoff, struct dso *dso)
    kmap = map__kmap(machine->vmlinux_maps[type]);

    struct kmap *kmap = (struct kmap *)(map + 1)
    Set the kmap structure points to machine->kmaps.
    kmap->kmaps = &machine->kmaps;
    MAP__NR_TYPES struct map vmlinux_maps
    |0 | 1|  2 | 3 | …. | MAP__NR_TYPES-1|
    stuct map *map; struct kmap *kmap;
    kmap->kmaps = machine->kmaps;
    map_groups__insert(&machine->kmaps, machine->vmlinux_maps[type]);
    __maps__insert(struct maps *maps, struct map *map)
    Insert new allocated map in machine->vmlinux[type] into kmaps in machine.
    note:

    struct machine {
        struct dsos      dsos;
        struct map_groups kmaps;
        struct map      *vmlinux_maps[MAP__NR_TYPES];
        u64          kernel_start;

    };
    struct map {
    struct dso        *dso;
    struct map_groups    *groups;
    ….
    };


    Now there is extra space of struct kmap after struct map in machine, and in witch kmaps(map_groups type) points to struct kmap of machine(type map_groups) itself. Another thing is
    1. the struct map_groups *group in the former struct map is initialized by NULL and then set to machine->kmaps;
    2. dso in struct map points to machine->dso
    3. struct map_groups *group in the later struct kmap points to struct map_groups in machine structure.
    machine {
    struct map vmlinux[MAP__NR_TYPES];
    //vmlinux[*]->dso = created dso;
    //(struct kmap *)(vmlinux[*]+1) -> kmaps= machine->kmaps;
    }
    struct kmap {

        struct ref_reloc_sym    *ref_reloc_sym;
        struct map_groups    *kmaps;
    };
    struct map_groups {
        struct maps     maps[MAP__NR_TYPES];
        struct machine     *machine;   // machine->kmaps -> machine = machine;
        atomic_t     refcnt;
    };
  • machine__create_modules(machine);
    modules__parse(modules, machine, machine__create_module)
    Get start address and names for all modules.

    1. struct map *map = machine__findnew_module_map(machine, start, name);
      For each modules, find out whether OS have already has module inserted to machine->dsos, if not, a new dso will be created with passed in module name and then inserted into machine->dsos.
      struct map *map = map_groups__find_by_name(&machine->kmaps, MAP__FUNCTION, m.name);
      if (map == NULL) //Can’t find map, so create one for this module
      struct dso *dso = machine__findnew_module_dso(machine, &m, filename);
      if (dso != NULL)
      Find out if there is existing dso for this module name, if not, create one. Module numbers linked to dso is counted by dso->refcnt.
      struct map *map = map__new2(start, dso, MAP__FUNCTION);
      map_groups__insert(&machine->kmaps, map);
    2. dso__kernel_module_get_build_id(map->dso, machine->root_dir);
      Still for each modules, read /sys/module/[MODULE_NAME]/notes/.note.gnu.build-id just like we did in sysfs__read_build_id(path, dso->build_id, sizeof(dso->build_id) in Symbol-minimal.c
      sysfs__read_build_id(filename, dso->build_id, sizeof(dso->build_id)
    3. machine__set_modules_path(machine);
      Go to /lib/modules/$(KENERL_VERSION)/, check all files including sub-directory if there is modules end with “.ko”, if yes, set the dso’s long_name to the module’s absolute path.kmod_path__parse_name(&m, dent->d_name);
      m->kmod = !strncmp(ext, “.ko”, 3);
      m->name = strdup(name)
      map_groups__set_module_path(mg, path, &m);
      Try to find module in (struct dso *)mg->maps by name. If succeed, set dso’s long_name with module’s absolute path.
      long_name = strdup(path);
      dso__set_long_name(map->dso, long_name, true);
  • map_groups__fixup_end(&machine->kmaps);
    Set prev->end = cur->start;
    last->end = ~0ULL;

Get back to __cmd_record

2. record__init_features(rec);
3. perf_evlist__prepare_workload(rec->evlist, &opts->target, argv, file->is_pipe,
workload_exec_failed_signal);

  • Start  a new process using fork() for the suffix parameters e.g. sleep 3. evlist->workload.pid = fork();
    Child process(cp) tells parent process(pp) by:
    close(child_ready_pipe[0]);
    close(go_pipe[1]);
    fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); // set go_pipe[0] to FD_CLOEXEC
    close(child_ready_pipe[1]);
    ret = read(go_pipe[0], &bf, 1)
    pp set cp go by
    close(child_ready_pipe[1]);
    close(go_pipe[0]);
    if (read(child_ready_pipe[0], &bf, 1) == -1) {
        goto out_close_pipes;
    };
    fcntl(go_pipe[1], F_SETFD, FD_CLOEXEC);// SET go_pipe[1] to FD_CLOEXEC
    Then cp runs execvp(argv[0], (char **)argv);

4. record__open(rec)

  • perf_evlist__config(rec.evlist, rec.opts);
    perf_can_comm_exec();
    perf_probe_api(perf_probe_comm_exec);
    perf_do_probe_api(perf_probe_comm_exec, cpu, try[]);
    //const char *try[] = {“cycles:u”, “instructions:u”, “cpu-clock:u”, NULL};
    perf_event_open_cloexec_flag
    fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags);
    //sys_perf_event_open(&evsel->attr, -1, 0, -1, 8); just a probe here
    //This syscall creates a file descriptor that allows measuring performance //information. Each file descriptor corresponds to one event measured;
    //for more details please visit here.
  • Open each of the fd for each cpu on each threads for every evsel on evlist
    evlist__for_each(evlist, pos) {

    perf_evsel__open(pos, pos->cpus, pos->threads)
    __perf_evsel__open(evsel, cpus, threads);
    Set the very file descriptor to the evsel->fd->content[]
    for (cpu = 0; cpu < cpus->nr; cpu++) {
        for (thread = 0; thread < nthreads; thread++) {
            FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
    pid,
    cpus->map[cpu], group_fd, flags);
    } // endof nthreads
    } // endof cpus->nr
    /*The pid and cpu arguments specifies which process and CPU to
    monitor.

    pid: 0 cpu: -1 measures the calling process for all cpu

    pid: 0 cpu: >= 0 measures the calling process for specified cpu

    pid: > 0 cpu: -1 measures the specified process for all cpu
    pid: > 0 cpu: >= 0 measures the specified process for specified cpu
    pid: -1 cpu: >= 0 measures the all process for specified cpu
    This requires CAP_SYS_ADMIN capability or a
    “/proc/sys/kernel/perf_event_paranoid value of less than 1.”
    pid: -1 cpu: -1 error.

    */

    #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
    static inline void *xyarray__entry(struct xyarray *xy, int x, int y)
    {return &xy->contents[x * xy->row_size + y * xy->entry_size];}
    #define FD(evsel, cpu, thread) (*(int *))xyarrary__entry(evsel->fd, cpu, thread){return evsel->fd->content[cpu * evsel->fd->row_size + thread * evsel->fd->entry_size]}
  • Created fds are stored in evsel->fd->contents[].
    Now perf got all fd with syscall in kernel space, we need to mmap them to userspace.
    perf_evlist__mmap_ex(evlist, opts->mmap_pages, false,
    opts->auxtrace_mmap_pages,
    (opts->auxtrace_snapshot_mode<0))
    perf_evlist__mmap_ex(evlist, 4294967295, false, 0, false)

    struct mmap_params mp = {
        .prot = PROT_READ | (overwrite ? 0 : PROT_WRITE),
    };

    perf_evlist__alloc_mmap(evlist)
    perf_evlist__alloc_pollfd(evlist)

    perf_evlist__mmap_per_cpu(evlist, &mp)

    for each online cpu: {
        int output = -1;
        for each thread:
    perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu, thread, &output))

    };

    perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu, thread, &output)) ->
    evlist__for_each(evlist, evsel) {
    __perf_evlist__mmap(evlist, cpu, mp, *output)->
    //*output = FD(evsel, cpu, thread)
    evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, mp->prot,
    MAP_SHARED, *output, 0);
    Then memory could be accessed through evlist->mmap[cpu].base

    auxtrace_mmap__mmap(&evlist->mmap[cpu].auxtrace_mmap,
    &mp->auxtrace_mp, evlist->mmap[cpu].base, *output)
    munmap(mm->base, mm->len)
    }

Then we need to write and map information to perf.data file.
5. perf_session__write_header(session, rec->evlist, fd, false);
Write all f_attr of evsel in evlist down to fd

  1.  lseek(fd, sizeof(f_header), SEEK_SET);
  2. evlist__for_each(session->evlist, evsel) {
    evsel->id_offset = lseek(fd, 0, SEEK_CUR);
    //set offset for each evsel in evlist
    do_write(fd, evsel->id, evsel->ids * sizeof(u64))
    fd | f_header | id_offset * nr | f_attr * nr | data        |
    // record all evsel’s id info into id_offset section.
  3. evlist__for_each(evlist, evsel) {
    f_attr = (struct perf_file_attr){
    .attr = evsel->attr,
    .ids  = {
    .offset = evsel->id_offset,
    .size   = evsel->ids * sizeof(u64),
    }
    };
    do_write(fd, &f_attr, sizeof(f_attr));
    // write f_attr above into fd
    }
    fd | f_header | id_offset * nr | f_attr * nr | data        |
    header->data_offset = lseek(fd, 0, SEEK_CUR);
    header->feat_offset = header->data_offset + header->data_size;
    // set offset for data and feat_offset
  4. do_write(fd, &f_header, sizeof(f_header));
    // write header of fd
    evlist__for_each(evlist, evsel) {
    f_attr = (struct perf_file_attr){
    .attr = evsel->attr,
    .ids  = {
    .offset = evsel->id_offset,
    .size   = evsel->ids * sizeof(u64),
    }
    };
    err = do_write(fd, &f_attr, sizeof(f_attr));
    }
    fd | f_header | id_offset * nr | f_attr * nr | data        |
    f_header = (struct perf_file_header){
    .magic       = PERF_MAGIC,
    .size       = sizeof(f_header),
    .attr_size = sizeof(f_attr),
    .attrs = {
    .offset = attr_offset,
    .size   = evlist->nr_entries * sizeof(f_attr),
    },
    .data = {
    .offset = header->data_offset,
    .size    = header->data_size,
    },
    /* event_types is ignored, store zeros */
    };
    fd | f_header | id_offset * nr | f_attr * nr | data        |
    attr_offset   data_offset  data_size
    attr_offset = lseek(fd, 0, SEEK_CUR);
    // set offset for attr in fd

6. perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,machine);
build a union perf_event *event;
size = snprintf(event->mmap.filename, sizeof(event->mmap.filename),”%s%s”,
mmap_name, kmap->ref_reloc_sym->name); //”[kernel.kallsyms]_text
event->mmap.header.size = (sizeof(event->mmap) –
(sizeof(event->mmap.filename) – size) + machine->id_hdr_size);
event->mmap.pgoff = kmap->ref_reloc_sym->addr;
event->mmap.start = map->start;
event->mmap.len   = map->end – event->mmap.start;
event->mmap.pid   = machine->pid;
Insert event to rec by calling process_synthesized_event();
record__write(rec, event, event->header.size);

7. perf_event__synthesize_modules(tool, process_synthesized_event,
machine)

struct mmap_event {
    struct perf_event_header header;
    u32 pid, tid;
    u64 start;
    u64 len;
    u64 pgoff;
    char filename[PATH_MAX];
};
union perf_event{
struct mmap_event        mmap;
}
union perf_event *event;

Create perf_events (as shown above) for all maps in machine->kmaps->maps[MAP__FUNCTION] respectively and fill in members like
mmap.header.type = PERF_RECORD_MMAP
MMAP |struct perf_event_header header| pid, tid| start | len | pgoff | filename |
size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
// Length of long_name in dso
event->mmap.header.size =
sizeof(event->mmap) – (sizeof(event->mmap.filename) – size))
// Calculate the real length of mmap and write into header.size, step one.
memset(event->mmap.filename + size, 0, machine->id_hdr_size);
event->mmap.header.size += machine->id_hdr_size;
// Calculate the real length of mmap, step two.
memset(event->mmap.filename + size, 0, machine->id_hdr_size);
Writer machine->id_hdr_size to filename+size part for filename need to store long_name with the first size part.
MMAP | header| pid, tid | start | len | pgoff | filename |
header.size
filename |machine->dso->long_name| machine->id_hdr_size|
mmap.start, mmap.len, mmap.pid, mmap.filename. Then insert them all to “perf.data” file.
MMAP | header | pid, tid | start | len | pgoff | filename |

8. __machine__synthesize_threads
Since we called perf with -a option, perf_event__synthesize_threads will be called.
For all process in /proc/
__event__synthesize_thread(comm_event, mmap_event, fork_event, pid, (int full)1, process, tool, machine, mmap_data, proc_map_timeout);
For every task(_pid) in /proc/pid/task/, create an comm_event, fork_event, mmap_event for each of them (perf_event__prepare_comm)and get COMM, tigd and ppid for pid in /proc/pid/status by calling perf_event__prepare_comm(comm_event, _pid, machine, &tgid, &ppid).
PID = 3665
COMM_EVENT
*tgid = 3665
*ppid = 2485

event->comm.pid = *tgid;(3665) // they share the same pid
event->comm.header.type = PERF_RECORD_COMM;

struct comm_event {
    struct perf_event_header {
        PERF_RECORD_COMM;
        size
    } header;
    u32 pid; 3665
    u32 tid; _pid in /proc/3665/task/
    char comm[16]; name+machine->id_hdr_size
    },
};

Then insert fork_event, comm_event to perf.data.
For the main thread, create an mmap_event and read /proc/pid/maps, each line printed will be recorded and then written into perf.data in terms of mmap_event.

perf_event__synthesize_mmap_events(tool, mmap_event, pid: 3665, tgid: 3665,
process, machine, mmap_data: false, proc_map_timeout: 500)
cat /proc/3665/maps
82a23000-82e75000 r-xp 00000000 08:01 134003    /lib/i386-linux-gnu/libnss_files-2.19.so
sscanf(bf, “%llx-%llx %s %llx %x:%x %u %sn”,
&event->mmap2.start, &event->mmap2.len, prot,
&event->mmap2.pgoff, &event->mmap2.maj,
&event->mmap2.min,
&ino, execname);
struct mmap2_event {
struct perf_event_header header;
u32 pid, tid; 3665, 3665
u64 start; 0x82a23000
u64 len; 452000 (event->mmap2.len -= event->mmap.start;)
u64 pgoff; 00000000
u32 maj; 08
u32 min; 01
u64 ino; 134003
u64 ino_generation;
u32 prot; 0x4 | 0x1
u32 flags; 0x02;
char filename[PATH_MAX]; “/lib/i386-linux-gnu/libnss_files-2.19.so”
};
char execname[PATH_MAX] =  ” /lib/i386-linux-gnu/libnss_files-2.19.so”
header.size = sizeof(event->mmap2)-sizeof(filename)+(strlen(filename)+1)+id_hdr_size

Goagent get “global name ‘SSLContext’ is not defined”

The thing is I updated my goagent from github with my ubuntu, however after that, every url begins with https through goagent is unaccessable.
Tracked log says: NameError: global name ‘SSLContext’ is not defined.
Check python installation package:
sudo apt-get install python-dev python-greenlet python-gevent python-vte python openssl python-crypto python-appindicator python-setuptools
Then: sudo easy_install -U gevent
Done!

Why is that?
gevent.ssl.SSLSocket.__init__()  tries to reference SSLContext which doesn’t exist in Python 2.7.8 yet, but it’s introduced in Python 2.7.9.
Bug fixed in the newest version of Python-gevent.
See here for more details.

HOW DID I FIX MY VPS AND RESCUE MY WORDPRESS DATA

Got my vps ddosed the other days, and so busy was my working days that I couldn’t spare my time to fix it. Now you see it just works fine, this article write down how I did.

On July 1st I received an email from my vps provider and they told me that I have over 10GB anonymous bit torrent data up/down load per day, hence they have to shut it down to avoid legal issues. Also, I was told that the hacker may left a backdoor even if I change my root password, so I could only reinstall my VPS system. The problem now is: how I could do to save my old home page. Luckily, they offered me an rescue mode, with which I can mount my old disk image, what’s worse is that I couldn’t use chroot. Terrible things just happened.

First of all, mount -t ext4 /media/temp /dev/xvdb and copy important files to my own host, including
wp-content/uploads                                                    //uploaded files
/etc/apache2/sites-available/www.haodong.org      //my apache settings
/usr/share/wordpress/wp-config.php                       //wordpress settings
/etc/wordpress/*                                                          //wordpress settings
wp-content/uploads/2015_xx_xx_database.sql      //back up data base, most important

Second, install a new operating system, and install the following programs.
apt-get install apache2 php5  mysql-server mysql-common mysql-client
apt-get install libapache2-mod-auth-mysql php5-mysql php5-gd
Start apache service
service apache2 start
install phpmyadmin and wordpress
apt-get install phpmyadmin wordpress
Now I have wordpress in /usr/share/wordpress.
Upload settings to the corresponding positions.
scp www.haodong.org root@dong:/etc/apache2/site-available/
a2ensite www.haodong.org
It seems apache2 has made a limitation on its web config file that it must ends with .conf so rename www.haodong.org to haodong.conf if you got “www.haodong.org does not exist
For /etc/apache2, which includes default website config containing /var/www/html, make my website work by a2dissite 000-default.conf since in apache2.conf there is

IncludeOptional sites-enabled/*.conf

scp wp-content/uploads root@dong:/usr/share/wordpress/uploads/
scp wp-config.php root@dong:/usr/share/wordpress/wp-config.php                      
scp etc/wordpress/* root@dong:/etc/wordpress

Add on Oct. 16 2015, it seems apache2 has changed its way of setting. Website folders are restrained to /var/www/html, so synchronize files in wordpress.

rsync -avP /usr/share/wordpress/  /var/www/html/

Make sure:
1. define(‘WPLANG’, ‘zh_CN’); exist in /etc/wordpress/wp-config.php
2. Enable write permition of htaccess since I need permenant link.
3. The location in /etc/apache2/sites-available/www.haodong.org is just the wordpress symbolic setting file.
4. Be careful on /etc/wordpress/config-xxx.php, which stored the name and password to mysql data file. Make sure you put them in a safe place, and if not, don’t hesitate to crash your head on a wall. :p
Technically, we finished reinstalling and enabling everthing, let’s try.
1. Open http://haodong.org/phpadmin and input your mysql user and password.(Should be set when you install mysql, like root:123456), if you forget everything about mysql, you could reset DB_NAME, DB_USER, DB_PASSWORD by
mysql -u root -p
CREATE DATABASE wordpress;
CREATE USER wordpressuser@localhost IDENTIFIED BY ‘password’;
GRANT ALL PRIVILEGES ON wordpress.* TO wordpressuser@localhost;
FLUSH PRIVILEGES;
2. upload the sql file we set aside, remember wordpress only support upload file under 2MB, so we need to gzip it to a xxx.sql.zip. After that, open http://haodong.org/wp-admin/install.php, set an administrator user:password pair, which will be stored in the wp-users table we just imported.
Should everything be done?
No.
3. Open www.haodong.org, oops, I forget my administrator user and password. How should I do? Edit the table wp-users table and set password field to “5d41402abc4b2a76b9719d911017c592” to reset my password to “hello”.
Login, and see how my new website going, but what I saw is an amount of messy code. Actually, the old mysql data file is stored in latin1, so before upload the data file, I need to set it to UTF8 in order to make Chinese characters looks well.
vi xxx_database.sql
:%s/latin1/UTF8/g
:wq
gzip xxx.sql and upload the file again with UTF-8 Unicode (utf8)  and utf8_general_ci
Here we got beautiful Chinese words. But wait, from ssh, I still saw Chinese names abnormly showed in my screen.
vi /var/lib/locales/supported.d/local
add
zh_CN.UTF-8 UTF-8
en_US.UTF-8 UTF-8
zh_CN.GBK GBK
zh_CN GB2312
vi /etc/environment
add
LANG=”zh_CN:UTF-8″
LANGUAGE=”zh_CN:zh:en_US:en”
LC_CTYPE=”zh_CN:UTF-8″
Then run locale-gen
Untill now, I could get access to all of my articles when I open my website. But there is one more thing. The media files could still not be touched. I could change uploads file to 777 but this may cause security issues, actually I don’t want to save my vps again. So what I do is:
Change uploads file to 777 temporarily and upload one picture, log on to my vps and see who owns wordpress as a user. And I got:
# ll wordpress/wp-conteng/uploads/xxx.jpg
www-data:www-data
OK, gotcha,
chown -R www-data:www-data /usr/share/wordpress
chmod 755 /usr/share/wordpress
Now everything works well.
Oh, there is still one more thing, I’d like to talk about is the security problem, I need to do something to avoid being hacked again.