Closed Bug 854204 Opened 11 years ago Closed 6 years ago

[Per app power metering] - Calculate CPU usage per process

Categories

(Firefox OS Graveyard :: General, defect)

ARM
Gonk (Firefox OS)
defect
Not set
normal

Tracking

(Not tracked)

RESOLVED WONTFIX

People

(Reporter: vchang, Assigned: gavin09)

References

Details

Attachments

(2 files, 10 obsolete files)

      No description provided.
Blocks: 854200
I studied the implementation of CPU in Android. 
The primary implementation is in processAppUsage() - http://androidxref.com/4.2.2_r1/xref/packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageSummary.java#433

userTime and systemTime can be obtained in /proc/<pid>/stats.
cpuSpeedTime can be obtained in /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
PowerProfile has the information of average power consumption. 

The general form of equation is
processPower = ratio[step1] * Time * powerCpuNormal[step1] + ... + ratio[stepk] * Time * powerCpuNormal[stepk]

ratio[step] = cpuStepTime[step]/ TotalTime
Time = userTime + systemTime
powerCpuNormal[stepk] is the information in powerProfile
Attached patch CPU power per app (obsolete) — Splinter Review
collect CPU user time and CPU system time per process.
Attachment #771277 - Flags: feedback?(vchang)
Attached patch CPU power per app (obsolete) — Splinter Review
support to analyze power consumption of CPU per webapp
Attachment #771277 - Attachment is obsolete: true
Attachment #771277 - Flags: feedback?(vchang)
Attachment #773151 - Flags: feedback?(vchang)
Attached patch (wip)PU power per app (obsolete) — Splinter Review
Attachment #773151 - Attachment is obsolete: true
Attachment #773151 - Flags: feedback?(vchang)
Attached patch CPU power per app (obsolete) — Splinter Review
Attachment #778288 - Attachment is obsolete: true
Attachment #780214 - Flags: feedback?(vchang)
Comment on attachment 780214 [details] [diff] [review]
CPU power per app

Review of attachment 780214 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/power/PowerStatsService.jsm
@@ +125,5 @@
> +        if (powerInfoArr.length < 3) {
> +          debug("The format of power-meter-info is not right.");
> +          break;
> +        }
> +        let index = powerInfoArr[0];

Can we valid the index value here ? Return error if the index is invalid.

@@ +138,5 @@
> +        } else if (index.substring(0, 7) == HTTP_URL) {
> +          this.createAppInfo(index, item, value);
> +        } else if (index.substring(0, 8) == HTTPS_URL) {
> +          this.createAppInfo(index, item, value);
> +        }

Just call this.createAppInfo(index, item, value) because we have validate the index.

@@ +280,5 @@
>        });
>      }
>    },
>  
> +  createAppInfo: function createAppInfo(index, item, value) {

The item should be "pid" only right ? 
Move "if (item !== "pid") return;" in the beginning of the function.

@@ +282,5 @@
>    },
>  
> +  createAppInfo: function createAppInfo(index, item, value) {
> +    // Check if appInfo exists this app info.
> +    if (appPowerInfo[index] == null) {

if (index in appPowerInfo)

@@ +287,5 @@
> +      debug("Add new app power info into appPowerInfo.");
> +      if (item == "pid") {
> +        appPowerInfo[index] = {pid: value[0],
> +                               tmpTime: 0,
> +                               time: 0};

What is the purpose of tmpTime and time ?

@@ +302,5 @@
> +  readProcPidStat: function readProcPidStat(manifestURL, pid) {
> +    let error = 0;
> +    let result = "";
> +
> +    if (pid === -1) {

Please use a constant for "-1", do we need to return an error here ? Please add comments here to explain why don't you return immediately.

@@ +311,5 @@
> +    debug("Path of procPath: " + procPath + ".");
> +
> +    let data = "";
> +    data = this.readFile(procPath);
> +    let statArr = data.split(/(\(.*?\)|[^",\s]+)(?= \w+\s*)/gi);

What is the staeArr ? Can you add a comment to explain it ?

@@ +313,5 @@
> +    let data = "";
> +    data = this.readFile(procPath);
> +    let statArr = data.split(/(\(.*?\)|[^",\s]+)(?= \w+\s*)/gi);
> +    let time = 0;
> +    //time = user time + system time

Nit: space after //, also add period at the end of sentence.

@@ +314,5 @@
> +    data = this.readFile(procPath);
> +    let statArr = data.split(/(\(.*?\)|[^",\s]+)(?= \w+\s*)/gi);
> +    let time = 0;
> +    //time = user time + system time
> +    time = parseInt(statArr[USERTIME_INDEX], DECIMAL) + 

Nit: trail white space.

@@ +323,5 @@
> +  readLastProc: function readLastProc(manifestURL) {
> +    debug("The last time of reading pid: " + appPowerInfo[manifestURL].pid + ".");
> +    let time = this.readProcPidStat(manifestURL, appPowerInfo[manifestURL].pid);
> +
> +    appPowerInfo[manifestURL].time += time;

Is this correct ?

@@ +325,5 @@
> +    let time = this.readProcPidStat(manifestURL, appPowerInfo[manifestURL].pid);
> +
> +    appPowerInfo[manifestURL].time += time;
> +    appPowerInfo[manifestURL].tmpTime = 0;
> +    appPowerInfo[manifestURL].pid = -1;

Please use a constant for "-1".

@@ +330,5 @@
> +  },
> +
> +  updateAppInfo: function updateAppInfo() {
> +    for (let i in appPowerInfo) {
> +      if (appPowerInfo[i].pid !== -1) {

Please use a constant for "-1".

@@ +331,5 @@
> +
> +  updateAppInfo: function updateAppInfo() {
> +    for (let i in appPowerInfo) {
> +      if (appPowerInfo[i].pid !== -1) {
> +        appPowerInfo[i].tmpTime = this.readProcPidStat(i, appPowerInfo[i].pid);

Why do you set the return value to tmpTime rather than time ?
Attachment #780214 - Flags: feedback?(vchang)
(In reply to Vincent Chang[:vchang] from comment #6)
> Comment on attachment 780214 [details] [diff] [review]
> CPU power per app
> 
> Review of attachment 780214 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: dom/power/PowerStatsService.jsm
> @@ +125,5 @@
> > +        if (powerInfoArr.length < 3) {
> > +          debug("The format of power-meter-info is not right.");
> > +          break;
> > +        }
> > +        let index = powerInfoArr[0];
> 
> Can we valid the index value here ? Return error if the index is invalid.

If a process is launched, appPowerInfo does not have the index yet. So, here only check if the index is APP_URL, HTTP_URL or HTTPS_URL. Or, is there any other thing need to validate? 

> @@ +138,5 @@
> > +        } else if (index.substring(0, 7) == HTTP_URL) {
> > +          this.createAppInfo(index, item, value);
> > +        } else if (index.substring(0, 8) == HTTPS_URL) {
> > +          this.createAppInfo(index, item, value);
> > +        }
> 
> Just call this.createAppInfo(index, item, value) because we have validate
> the index.

There will have updateSystemInfo(index, item, value) after system component parts are done. 

> @@ +280,5 @@
> >        });
> >      }
> >    },
> >  
> > +  createAppInfo: function createAppInfo(index, item, value) {
> 
> The item should be "pid" only right ? 
> Move "if (item !== "pid") return;" in the beginning of the function.
> 
> @@ +282,5 @@
> >    },
> >  
> > +  createAppInfo: function createAppInfo(index, item, value) {
> > +    // Check if appInfo exists this app info.
> > +    if (appPowerInfo[index] == null) {
> 
> if (index in appPowerInfo)
> 

I think the function name: createAppInfo may be confusing. I change the function name to collectAppInfo. collectAppInfo function will not only collect pid related information. collectAppInfo is responsible for collecting application related information. I will refine this.

> @@ +287,5 @@
> > +      debug("Add new app power info into appPowerInfo.");
> > +      if (item == "pid") {
> > +        appPowerInfo[index] = {pid: value[0],
> > +                               tmpTime: 0,
> > +                               time: 0};
> 
> What is the purpose of tmpTime and time ?
> 

tmpTime is CPU power consumption of a runnig process. The user time and system time of a running process in /proc/<pid>/stats are increasing. So, tmpTime is temporary. PowerStatsService add tmpTime into time when the process is terminated.

I will write comments here.

> @@ +302,5 @@
> > +  readProcPidStat: function readProcPidStat(manifestURL, pid) {
> > +    let error = 0;
> > +    let result = "";
> > +
> > +    if (pid === -1) {
> 
> Please use a constant for "-1", do we need to return an error here ? Please
> add comments here to explain why don't you return immediately.
> 

I will refine this. Thanks.

> @@ +311,5 @@
> > +    debug("Path of procPath: " + procPath + ".");
> > +
> > +    let data = "";
> > +    data = this.readFile(procPath);
> > +    let statArr = data.split(/(\(.*?\)|[^",\s]+)(?= \w+\s*)/gi);
> 
> What is the staeArr ? Can you add a comment to explain it ?
> 

I will write comments here. Thanks.

> @@ +323,5 @@
> > +  readLastProc: function readLastProc(manifestURL) {
> > +    debug("The last time of reading pid: " + appPowerInfo[manifestURL].pid + ".");
> > +    let time = this.readProcPidStat(manifestURL, appPowerInfo[manifestURL].pid);
> > +
> > +    appPowerInfo[manifestURL].time += time;
> 
> Is this correct ?
> 

I think it is correct here. Is there any problems?
Thanks.

> @@ +331,5 @@
> > +
> > +  updateAppInfo: function updateAppInfo() {
> > +    for (let i in appPowerInfo) {
> > +      if (appPowerInfo[i].pid !== -1) {
> > +        appPowerInfo[i].tmpTime = this.readProcPidStat(i, appPowerInfo[i].pid);
> 
> Why do you set the return value to tmpTime rather than time ?

Update applications information only need to update running processes.


Thanks for the advice.
I will refine this as soon as possible.
Attached patch CPU power per app (obsolete) — Splinter Review
Before this patch, PowerStatsService collects time and saves into appPowerInfo. Originally, we plan to calculate power consumption in gaia. Now, PowerStatsService calculates power consumption and saves into appPowerInfo. cpuPower function is responsible for calculating CPU power consumption per application.
Attachment #780214 - Attachment is obsolete: true
Attachment #786317 - Flags: feedback?(vchang)
Attached patch CPU power per app (obsolete) — Splinter Review
Fix bugs. When gecko terminates brower, mAppManifestURL is empty. PowerStatsService does not receive browser's manifestURL.
Attachment #786317 - Attachment is obsolete: true
Attachment #786317 - Flags: feedback?(vchang)
Attachment #786742 - Flags: feedback?(vchang)
Assignee: nobody → glai
Fix bugs. When closing tabs in browser, ContentParent does not notify PowerStatsService.
Attachment #786742 - Attachment is obsolete: true
Attachment #786742 - Flags: feedback?(vchang)
Attachment #791076 - Flags: feedback?(vchang)
Fix bugs. Browser is a special case. Each tab has its own process id (pid), and these tabs share the same manifestURL of browser. So, PowerStatsService use an array to record the pid of tabs.
Attachment #791080 - Flags: feedback?(vchang)
No longer blocks: 854200
Depends on: 854200, 854202
Fix nit.
Attachment #791080 - Attachment is obsolete: true
Attachment #791080 - Flags: feedback?(vchang)
Attachment #791205 - Flags: feedback?(vchang)
Comment on attachment 791205 [details] [diff] [review]
Part 1: Estimate per app CPU power consumption.

Review of attachment 791205 [details] [diff] [review]:
-----------------------------------------------------------------

Drop the feedback for now, since you have to modify readProfile() to async call.
Attachment #791205 - Flags: feedback?(vchang)
Comment on attachment 791076 [details] [diff] [review]
Part 0: Collect application information from ContentParent.

Review of attachment 791076 [details] [diff] [review]:
-----------------------------------------------------------------

Basically, I am fine with this patch. But I feel like I am not capable to feedback this patch. Redirect to Justin.
Attachment #791076 - Flags: feedback?(vchang) → feedback?(justin.lebar+bug)
ContentParent already notifies the observer service when it's created, in ContentParent::Init().

I think the appropriate thing to do would be to add the pid and manifest URL to that notification, and to the shutdown notification.

See ContentParent::ActorDestroy for an example of how to add metadata to an observer notification  by way of nsHashPropertyBag.
Attachment #791076 - Flags: feedback?(justin.lebar+bug) → feedback-
(In reply to Justin Lebar [:jlebar] from comment #15)
> ContentParent already notifies the observer service when it's created, in
> ContentParent::Init().
> 
> I think the appropriate thing to do would be to add the pid and manifest URL
> to that notification, and to the shutdown notification.
> 
> See ContentParent::ActorDestroy for an example of how to add metadata to an
> observer notification  by way of nsHashPropertyBag.

Thanks for your help, we'll check it.
Assignee: glai → gavin09
Modify read file related functions to async call.
Attachment #796499 - Flags: feedback?(vchang)
Hi Justin,                                                                      
                                                                                
Thanks for your help!                                                           
                                                                                
I add metadata to ipc:content-shutdown notification by way of nsHashPropertyBag.       
And, it works great.

But, I have some problems to get mAppManifestURL by observing ipc:content-created notification. The mAppManifestURL in ContentParent::Init() is {{template}}.
It seems that the content of the application have not loaded into the process yet.
                                                                                
Is it ok to create a notification to get the correct mAppManifestURL and pid?   
What do you think?
Attachment #797152 - Flags: feedback?(justin.lebar+bug)
> It seems that the content of the application have not loaded into the process yet.

Ah, indeed.

We should count power used by the {{template}} process towards "system", or whatever we're calling that.

It sounds like you should add a notification which is fired when we change that name, and then update the power stuff accordingly.

Note that my last day at Mozilla is tomorrow, so you'll need to find someone else to help out with this.  bent and khuey are probably the people who know this code the best.
Comment on attachment 797152 [details] [diff] [review]
(wip)Part 0: Collect application information from ContentParent.

This is the right idea, although there are still kinks to work out (the previous comment, and also the hardcoding of the browser app's URI as two examples).
Attachment #797152 - Flags: feedback?(justin.lebar+bug) → feedback+
(In reply to Justin Lebar [:jlebar] from comment #19)
> > It seems that the content of the application have not loaded into the process yet.
> 
> Ah, indeed.
> 
> We should count power used by the {{template}} process towards "system", or
> whatever we're calling that.
> 
> It sounds like you should add a notification which is fired when we change
> that name, and then update the power stuff accordingly.
> 
> Note that my last day at Mozilla is tomorrow, so you'll need to find someone
> else to help out with this.  bent and khuey are probably the people who know
> this code the best.

Thanks for your help.
Attachment #791076 - Attachment is obsolete: true
Attachment #791205 - Attachment is obsolete: true
Depends on: 913422
Get data by way of nsHashPropertyBag.
Attachment #796499 - Attachment is obsolete: true
Attachment #796499 - Flags: feedback?(vchang)
No longer depends on: 913422
No longer depends on: 854202
Hi Kyle:

I got a question: is that allowed to perform a file read in contentParent?

Here is the reason:

In order to calculate CPU power consumption, I want to get the execution time of an app (content process) by reading the status information from "/proc/[pid]/stat".

In my previous design, I let the b2g process observe the "ipc:content-shutdown" notification. When an app is terminated, the b2g process gets the notification (with the pid of that app) and reads the status information from "/proc/[pid]/stat". But, there is a chance that the b2g process fails to read the status information because the target process may be destroyed before the b2g process performs the read operation.

I think an alternative solution is to read the status information earlier and send the status information with the "ipc:content-shutdown" notifiction. So, is that possible to add some codes in ContentParent::ActorDestroy() to read the status information? (and send the status information to the b2g process with "ipc:content-shutdown" notification?)

Thanks.
Flags: needinfo?(khuey)
I don't understand your question.  ContentParent only exists in the b2g process.  By the time ContentParent::ActorDestroy is called, the other process may be gone already.  So I don't see how adding code in ContentParent::ActorDestroy to read from /proc/pid/stat helps solve your problem.
Flags: needinfo?(khuey)
Firefox OS is not being worked on
Status: NEW → RESOLVED
Closed: 6 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: