Commit 13b06b78 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branches 'pm-opp', 'pm-misc', 'pm-avs' and 'pm-tools'

* pm-opp:
  opp: Don't use IS_ERR on invalid supplies
  opp: Make dev_pm_opp_set_rate() handle freq = 0 to drop performance votes
  opp: Don't overwrite rounded clk rate
  opp: Allocate genpd_virt_devs from dev_pm_opp_attach_genpd()
  opp: Attach genpds to devices from within OPP core

* pm-misc:
  PM / clk: Remove error message on out-of-memory condition
  drivers: base: power: clock_ops: Use of_clk_get_parent_count()

* pm-avs:
  power: avs: smartreflex: no need to check return value of debugfs_create functions

* pm-tools:
  cpupower : frequency-set -r option misses the last cpu in related cpu list
  cpupower: correct spelling of interval
  Add README and update pm-graph and sleepgraph docs
  Update to pm-graph 5.4
  Update to pm-graph 5.3
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/pm_clock.h> #include <linux/pm_clock.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/of_clk.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
...@@ -92,8 +93,6 @@ static int __pm_clk_add(struct device *dev, const char *con_id, ...@@ -92,8 +93,6 @@ static int __pm_clk_add(struct device *dev, const char *con_id,
if (con_id) { if (con_id) {
ce->con_id = kstrdup(con_id, GFP_KERNEL); ce->con_id = kstrdup(con_id, GFP_KERNEL);
if (!ce->con_id) { if (!ce->con_id) {
dev_err(dev,
"Not enough memory for clock connection ID.\n");
kfree(ce); kfree(ce);
return -ENOMEM; return -ENOMEM;
} }
...@@ -195,8 +194,7 @@ int of_pm_clk_add_clks(struct device *dev) ...@@ -195,8 +194,7 @@ int of_pm_clk_add_clks(struct device *dev)
if (!dev || !dev->of_node) if (!dev || !dev->of_node)
return -EINVAL; return -EINVAL;
count = of_count_phandle_with_args(dev->of_node, "clocks", count = of_clk_get_parent_count(dev->of_node);
"#clock-cells");
if (count <= 0) if (count <= 0)
return -ENODEV; return -ENODEV;
......
...@@ -682,7 +682,7 @@ static int _set_opp_custom(const struct opp_table *opp_table, ...@@ -682,7 +682,7 @@ static int _set_opp_custom(const struct opp_table *opp_table,
data->old_opp.rate = old_freq; data->old_opp.rate = old_freq;
size = sizeof(*old_supply) * opp_table->regulator_count; size = sizeof(*old_supply) * opp_table->regulator_count;
if (IS_ERR(old_supply)) if (!old_supply)
memset(data->old_opp.supplies, 0, size); memset(data->old_opp.supplies, 0, size);
else else
memcpy(data->old_opp.supplies, old_supply, size); memcpy(data->old_opp.supplies, old_supply, size);
...@@ -708,7 +708,7 @@ static int _set_required_opps(struct device *dev, ...@@ -708,7 +708,7 @@ static int _set_required_opps(struct device *dev,
/* Single genpd case */ /* Single genpd case */
if (!genpd_virt_devs) { if (!genpd_virt_devs) {
pstate = opp->required_opps[0]->pstate; pstate = likely(opp) ? opp->required_opps[0]->pstate : 0;
ret = dev_pm_genpd_set_performance_state(dev, pstate); ret = dev_pm_genpd_set_performance_state(dev, pstate);
if (ret) { if (ret) {
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n", dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
...@@ -726,7 +726,7 @@ static int _set_required_opps(struct device *dev, ...@@ -726,7 +726,7 @@ static int _set_required_opps(struct device *dev,
mutex_lock(&opp_table->genpd_virt_dev_lock); mutex_lock(&opp_table->genpd_virt_dev_lock);
for (i = 0; i < opp_table->required_opp_count; i++) { for (i = 0; i < opp_table->required_opp_count; i++) {
pstate = opp->required_opps[i]->pstate; pstate = likely(opp) ? opp->required_opps[i]->pstate : 0;
if (!genpd_virt_devs[i]) if (!genpd_virt_devs[i])
continue; continue;
...@@ -748,29 +748,37 @@ static int _set_required_opps(struct device *dev, ...@@ -748,29 +748,37 @@ static int _set_required_opps(struct device *dev,
* @dev: device for which we do this operation * @dev: device for which we do this operation
* @target_freq: frequency to achieve * @target_freq: frequency to achieve
* *
* This configures the power-supplies and clock source to the levels specified * This configures the power-supplies to the levels specified by the OPP
* by the OPP corresponding to the target_freq. * corresponding to the target_freq, and programs the clock to a value <=
* target_freq, as rounded by clk_round_rate(). Device wanting to run at fmax
* provided by the opp, should have already rounded to the target OPP's
* frequency.
*/ */
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{ {
struct opp_table *opp_table; struct opp_table *opp_table;
unsigned long freq, old_freq; unsigned long freq, old_freq, temp_freq;
struct dev_pm_opp *old_opp, *opp; struct dev_pm_opp *old_opp, *opp;
struct clk *clk; struct clk *clk;
int ret; int ret;
if (unlikely(!target_freq)) {
dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
target_freq);
return -EINVAL;
}
opp_table = _find_opp_table(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) { if (IS_ERR(opp_table)) {
dev_err(dev, "%s: device opp doesn't exist\n", __func__); dev_err(dev, "%s: device opp doesn't exist\n", __func__);
return PTR_ERR(opp_table); return PTR_ERR(opp_table);
} }
if (unlikely(!target_freq)) {
if (opp_table->required_opp_tables) {
ret = _set_required_opps(dev, opp_table, NULL);
} else {
dev_err(dev, "target frequency can't be 0\n");
ret = -EINVAL;
}
goto put_opp_table;
}
clk = opp_table->clk; clk = opp_table->clk;
if (IS_ERR(clk)) { if (IS_ERR(clk)) {
dev_err(dev, "%s: No clock available for the device\n", dev_err(dev, "%s: No clock available for the device\n",
...@@ -793,13 +801,15 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) ...@@ -793,13 +801,15 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
goto put_opp_table; goto put_opp_table;
} }
old_opp = _find_freq_ceil(opp_table, &old_freq); temp_freq = old_freq;
old_opp = _find_freq_ceil(opp_table, &temp_freq);
if (IS_ERR(old_opp)) { if (IS_ERR(old_opp)) {
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n", dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
__func__, old_freq, PTR_ERR(old_opp)); __func__, old_freq, PTR_ERR(old_opp));
} }
opp = _find_freq_ceil(opp_table, &freq); temp_freq = freq;
opp = _find_freq_ceil(opp_table, &temp_freq);
if (IS_ERR(opp)) { if (IS_ERR(opp)) {
ret = PTR_ERR(opp); ret = PTR_ERR(opp);
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n", dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
...@@ -1741,91 +1751,137 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) ...@@ -1741,91 +1751,137 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper); EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
static void _opp_detach_genpd(struct opp_table *opp_table)
{
int index;
for (index = 0; index < opp_table->required_opp_count; index++) {
if (!opp_table->genpd_virt_devs[index])
continue;
dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false);
opp_table->genpd_virt_devs[index] = NULL;
}
kfree(opp_table->genpd_virt_devs);
opp_table->genpd_virt_devs = NULL;
}
/** /**
* dev_pm_opp_set_genpd_virt_dev - Set virtual genpd device for an index * dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer
* @dev: Consumer device for which the genpd device is getting set. * @dev: Consumer device for which the genpd is getting attached.
* @virt_dev: virtual genpd device. * @names: Null terminated array of pointers containing names of genpd to attach.
* @index: index.
* *
* Multiple generic power domains for a device are supported with the help of * Multiple generic power domains for a device are supported with the help of
* virtual genpd devices, which are created for each consumer device - genpd * virtual genpd devices, which are created for each consumer device - genpd
* pair. These are the device structures which are attached to the power domain * pair. These are the device structures which are attached to the power domain
* and are required by the OPP core to set the performance state of the genpd. * and are required by the OPP core to set the performance state of the genpd.
* The same API also works for the case where single genpd is available and so
* we don't need to support that separately.
* *
* This helper will normally be called by the consumer driver of the device * This helper will normally be called by the consumer driver of the device
* "dev", as only that has details of the genpd devices. * "dev", as only that has details of the genpd names.
* *
* This helper needs to be called once for each of those virtual devices, but * This helper needs to be called once with a list of all genpd to attach.
* only if multiple domains are available for a device. Otherwise the original * Otherwise the original device structure will be used instead by the OPP core.
* device structure will be used instead by the OPP core.
*/ */
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
struct device *virt_dev,
int index)
{ {
struct opp_table *opp_table; struct opp_table *opp_table;
struct device *virt_dev;
int index, ret = -EINVAL;
const char **name = names;
opp_table = dev_pm_opp_get_opp_table(dev); opp_table = dev_pm_opp_get_opp_table(dev);
if (!opp_table) if (!opp_table)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
/*
* If the genpd's OPP table isn't already initialized, parsing of the
* required-opps fail for dev. We should retry this after genpd's OPP
* table is added.
*/
if (!opp_table->required_opp_count) {
ret = -EPROBE_DEFER;
goto put_table;
}
mutex_lock(&opp_table->genpd_virt_dev_lock); mutex_lock(&opp_table->genpd_virt_dev_lock);
if (unlikely(!opp_table->genpd_virt_devs || opp_table->genpd_virt_devs = kcalloc(opp_table->required_opp_count,
index >= opp_table->required_opp_count || sizeof(*opp_table->genpd_virt_devs),
opp_table->genpd_virt_devs[index])) { GFP_KERNEL);
if (!opp_table->genpd_virt_devs)
goto unlock;
dev_err(dev, "Invalid request to set required device\n"); while (*name) {
dev_pm_opp_put_opp_table(opp_table); index = of_property_match_string(dev->of_node,
mutex_unlock(&opp_table->genpd_virt_dev_lock); "power-domain-names", *name);
if (index < 0) {
dev_err(dev, "Failed to find power domain: %s (%d)\n",
*name, index);
goto err;
}
return ERR_PTR(-EINVAL); if (index >= opp_table->required_opp_count) {
dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n",
*name, opp_table->required_opp_count, index);
goto err;
}
if (opp_table->genpd_virt_devs[index]) {
dev_err(dev, "Genpd virtual device already set %s\n",
*name);
goto err;
}
virt_dev = dev_pm_domain_attach_by_name(dev, *name);
if (IS_ERR(virt_dev)) {
ret = PTR_ERR(virt_dev);
dev_err(dev, "Couldn't attach to pm_domain: %d\n", ret);
goto err;
} }
opp_table->genpd_virt_devs[index] = virt_dev; opp_table->genpd_virt_devs[index] = virt_dev;
name++;
}
mutex_unlock(&opp_table->genpd_virt_dev_lock); mutex_unlock(&opp_table->genpd_virt_dev_lock);
return opp_table; return opp_table;
err:
_opp_detach_genpd(opp_table);
unlock:
mutex_unlock(&opp_table->genpd_virt_dev_lock);
put_table:
dev_pm_opp_put_opp_table(opp_table);
return ERR_PTR(ret);
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_attach_genpd);
/** /**
* dev_pm_opp_put_genpd_virt_dev() - Releases resources blocked for genpd device. * dev_pm_opp_detach_genpd() - Detach genpd(s) from the device.
* @opp_table: OPP table returned by dev_pm_opp_set_genpd_virt_dev(). * @opp_table: OPP table returned by dev_pm_opp_attach_genpd().
* @virt_dev: virtual genpd device. *
* * This detaches the genpd(s), resets the virtual device pointers, and puts the
* This releases the resource previously acquired with a call to * OPP table.
* dev_pm_opp_set_genpd_virt_dev(). The consumer driver shall call this helper
* if it doesn't want OPP core to update performance state of a power domain
* anymore.
*/ */
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, void dev_pm_opp_detach_genpd(struct opp_table *opp_table)
struct device *virt_dev)
{ {
int i;
/* /*
* Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting * Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting
* used in parallel. * used in parallel.
*/ */
mutex_lock(&opp_table->genpd_virt_dev_lock); mutex_lock(&opp_table->genpd_virt_dev_lock);
_opp_detach_genpd(opp_table);
for (i = 0; i < opp_table->required_opp_count; i++) {
if (opp_table->genpd_virt_devs[i] != virt_dev)
continue;
opp_table->genpd_virt_devs[i] = NULL;
dev_pm_opp_put_opp_table(opp_table);
/* Drop the vote */
dev_pm_genpd_set_performance_state(virt_dev, 0);
break;
}
mutex_unlock(&opp_table->genpd_virt_dev_lock); mutex_unlock(&opp_table->genpd_virt_dev_lock);
if (unlikely(i == opp_table->required_opp_count)) dev_pm_opp_put_opp_table(opp_table);
dev_err(virt_dev, "Failed to find required device entry\n");
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_detach_genpd);
/** /**
* dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table. * dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
......
...@@ -138,7 +138,6 @@ static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np) ...@@ -138,7 +138,6 @@ static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
static void _opp_table_free_required_tables(struct opp_table *opp_table) static void _opp_table_free_required_tables(struct opp_table *opp_table)
{ {
struct opp_table **required_opp_tables = opp_table->required_opp_tables; struct opp_table **required_opp_tables = opp_table->required_opp_tables;
struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
int i; int i;
if (!required_opp_tables) if (!required_opp_tables)
...@@ -152,10 +151,8 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table) ...@@ -152,10 +151,8 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table)
} }
kfree(required_opp_tables); kfree(required_opp_tables);
kfree(genpd_virt_devs);
opp_table->required_opp_count = 0; opp_table->required_opp_count = 0;
opp_table->genpd_virt_devs = NULL;
opp_table->required_opp_tables = NULL; opp_table->required_opp_tables = NULL;
} }
...@@ -168,9 +165,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, ...@@ -168,9 +165,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
struct device_node *opp_np) struct device_node *opp_np)
{ {
struct opp_table **required_opp_tables; struct opp_table **required_opp_tables;
struct device **genpd_virt_devs = NULL;
struct device_node *required_np, *np; struct device_node *required_np, *np;
int count, count_pd, i; int count, i;
/* Traversing the first OPP node is all we need */ /* Traversing the first OPP node is all we need */
np = of_get_next_available_child(opp_np, NULL); np = of_get_next_available_child(opp_np, NULL);
...@@ -183,33 +179,11 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table, ...@@ -183,33 +179,11 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
if (!count) if (!count)
goto put_np; goto put_np;
/*
* Check the number of power-domains to know if we need to deal
* with virtual devices. In some cases we have devices with multiple
* power domains but with only one of them being scalable, hence
* 'count' could be 1, but we still have to deal with multiple genpds
* and virtual devices.
*/
count_pd = of_count_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells");
if (!count_pd)
goto put_np;
if (count_pd > 1) {
genpd_virt_devs = kcalloc(count, sizeof(*genpd_virt_devs),
GFP_KERNEL);
if (!genpd_virt_devs)
goto put_np;
}
required_opp_tables = kcalloc(count, sizeof(*required_opp_tables), required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
GFP_KERNEL); GFP_KERNEL);
if (!required_opp_tables) { if (!required_opp_tables)
kfree(genpd_virt_devs);
goto put_np; goto put_np;
}
opp_table->genpd_virt_devs = genpd_virt_devs;
opp_table->required_opp_tables = required_opp_tables; opp_table->required_opp_tables = required_opp_tables;
opp_table->required_opp_count = count; opp_table->required_opp_count = count;
......
...@@ -899,38 +899,19 @@ static int omap_sr_probe(struct platform_device *pdev) ...@@ -899,38 +899,19 @@ static int omap_sr_probe(struct platform_device *pdev)
} }
dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n", __func__); dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n", __func__);
if (!sr_dbg_dir) { if (!sr_dbg_dir)
sr_dbg_dir = debugfs_create_dir("smartreflex", NULL); sr_dbg_dir = debugfs_create_dir("smartreflex", NULL);
if (IS_ERR_OR_NULL(sr_dbg_dir)) {
ret = PTR_ERR(sr_dbg_dir);
pr_err("%s:sr debugfs dir creation failed(%d)\n",
__func__, ret);
goto err_list_del;
}
}
sr_info->dbg_dir = debugfs_create_dir(sr_info->name, sr_dbg_dir); sr_info->dbg_dir = debugfs_create_dir(sr_info->name, sr_dbg_dir);
if (IS_ERR_OR_NULL(sr_info->dbg_dir)) {
dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n",
__func__);
ret = PTR_ERR(sr_info->dbg_dir);
goto err_debugfs;
}
(void) debugfs_create_file("autocomp", S_IRUGO | S_IWUSR, debugfs_create_file("autocomp", S_IRUGO | S_IWUSR, sr_info->dbg_dir,
sr_info->dbg_dir, (void *)sr_info, &pm_sr_fops); (void *)sr_info, &pm_sr_fops);
(void) debugfs_create_x32("errweight", S_IRUGO, sr_info->dbg_dir, debugfs_create_x32("errweight", S_IRUGO, sr_info->dbg_dir,
&sr_info->err_weight); &sr_info->err_weight);
(void) debugfs_create_x32("errmaxlimit", S_IRUGO, sr_info->dbg_dir, debugfs_create_x32("errmaxlimit", S_IRUGO, sr_info->dbg_dir,
&sr_info->err_maxlimit); &sr_info->err_maxlimit);
nvalue_dir = debugfs_create_dir("nvalue", sr_info->dbg_dir); nvalue_dir = debugfs_create_dir("nvalue", sr_info->dbg_dir);
if (IS_ERR_OR_NULL(nvalue_dir)) {
dev_err(&pdev->dev, "%s: Unable to create debugfs directory for n-values\n",
__func__);
ret = PTR_ERR(nvalue_dir);
goto err_debugfs;
}
if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) { if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) {
dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n", dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n",
...@@ -945,11 +926,11 @@ static int omap_sr_probe(struct platform_device *pdev) ...@@ -945,11 +926,11 @@ static int omap_sr_probe(struct platform_device *pdev)
snprintf(name, sizeof(name), "volt_%lu", snprintf(name, sizeof(name), "volt_%lu",
sr_info->nvalue_table[i].volt_nominal); sr_info->nvalue_table[i].volt_nominal);
(void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir, debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir,
&(sr_info->nvalue_table[i].nvalue)); &(sr_info->nvalue_table[i].nvalue));
snprintf(name, sizeof(name), "errminlimit_%lu", snprintf(name, sizeof(name), "errminlimit_%lu",
sr_info->nvalue_table[i].volt_nominal); sr_info->nvalue_table[i].volt_nominal);
(void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir, debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir,
&(sr_info->nvalue_table[i].errminlimit)); &(sr_info->nvalue_table[i].errminlimit));
} }
......
...@@ -128,8 +128,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); ...@@ -128,8 +128,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
void dev_pm_opp_put_clkname(struct opp_table *opp_table); void dev_pm_opp_put_clkname(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index); struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names);
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev); void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
...@@ -292,12 +292,12 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const ...@@ -292,12 +292,12 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {} static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
static inline struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index) static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
{ {
return ERR_PTR(-ENOTSUPP); return ERR_PTR(-ENOTSUPP);
} }
static inline void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev) {} static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {}
static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate) static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate)
{ {
......
...@@ -61,7 +61,7 @@ Only display specific monitors. Use the monitor string(s) provided by \-l option ...@@ -61,7 +61,7 @@ Only display specific monitors. Use the monitor string(s) provided by \-l option
.PP .PP
\-i seconds \-i seconds
.RS 4 .RS 4
Measure intervall. Measure interval.
.RE .RE
.PP .PP
\-c \-c
......
...@@ -98,7 +98,7 @@ msgstr "" ...@@ -98,7 +98,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74 #: utils/idle_monitor/cpupower-monitor.c:74
#, c-format #, c-format
msgid "\t -i: time intervall to measure for in seconds (default 1)\n" msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr "" msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75 #: utils/idle_monitor/cpupower-monitor.c:75
......
...@@ -95,7 +95,7 @@ msgstr "" ...@@ -95,7 +95,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74 #: utils/idle_monitor/cpupower-monitor.c:74
#, c-format #, c-format
msgid "\t -i: time intervall to measure for in seconds (default 1)\n" msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr "" msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75 #: utils/idle_monitor/cpupower-monitor.c:75
......
...@@ -95,7 +95,7 @@ msgstr "" ...@@ -95,7 +95,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74 #: utils/idle_monitor/cpupower-monitor.c:74
#, c-format #, c-format
msgid "\t -i: time intervall to measure for in seconds (default 1)\n" msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr "" msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75 #: utils/idle_monitor/cpupower-monitor.c:75
......
...@@ -95,7 +95,7 @@ msgstr "" ...@@ -95,7 +95,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74 #: utils/idle_monitor/cpupower-monitor.c:74
#, c-format #, c-format
msgid "\t -i: time intervall to measure for in seconds (default 1)\n" msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr "" msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75 #: utils/idle_monitor/cpupower-monitor.c:75
......
...@@ -93,7 +93,7 @@ msgstr "" ...@@ -93,7 +93,7 @@ msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:74 #: utils/idle_monitor/cpupower-monitor.c:74
#, c-format #, c-format
msgid "\t -i: time intervall to measure for in seconds (default 1)\n" msgid "\t -i: time interval to measure for in seconds (default 1)\n"
msgstr "" msgstr ""
#: utils/idle_monitor/cpupower-monitor.c:75 #: utils/idle_monitor/cpupower-monitor.c:75
......
...@@ -305,6 +305,8 @@ int cmd_freq_set(int argc, char **argv) ...@@ -305,6 +305,8 @@ int cmd_freq_set(int argc, char **argv)
bitmask_setbit(cpus_chosen, cpus->cpu); bitmask_setbit(cpus_chosen, cpus->cpu);
cpus = cpus->next; cpus = cpus->next;
} }
/* Set the last cpu in related cpus list */
bitmask_setbit(cpus_chosen, cpus->cpu);
cpufreq_put_related_cpus(cpus); cpufreq_put_related_cpus(cpus);
} }
} }
......
p m - g r a p h
pm-graph: suspend/resume/boot timing analysis tools
Version: 5.4
Author: Todd Brandt <todd.e.brandt@intel.com>
Home Page: https://01.org/pm-graph
Report bugs/issues at bugzilla.kernel.org Tools/pm-graph
- https://bugzilla.kernel.org/buglist.cgi?component=pm-graph&product=Tools
Full documentation available online & in man pages
- Getting Started:
https://01.org/pm-graph/documentation/getting-started
- Config File Format:
https://01.org/pm-graph/documentation/3-config-file-format
- upstream version in git:
https://github.com/intel/pm-graph/
Table of Contents
- Overview
- Setup
- Usage
- Basic Usage
- Dev Mode Usage
- Proc Mode Usage
- Configuration Files
- Usage Examples
- Config File Options
- Custom Timeline Entries
- Adding/Editing Timeline Functions
- Adding/Editing Dev Timeline Source Functions
- Verifying your Custom Functions
- Testing on consumer linux Operating Systems
- Android
------------------------------------------------------------------
| OVERVIEW |
------------------------------------------------------------------
This tool suite is designed to assist kernel and OS developers in optimizing
their linux stack's suspend/resume & boot time. Using a kernel image built
with a few extra options enabled, the tools will execute a suspend or boot,
and will capture dmesg and ftrace data. This data is transformed into a set of
timelines and a callgraph to give a quick and detailed view of which devices
and kernel processes are taking the most time in suspend/resume & boot.
------------------------------------------------------------------
| SETUP |
------------------------------------------------------------------
These packages are required to execute the scripts
- python
- python-requests
Ubuntu:
sudo apt-get install python python-requests
Fedora:
sudo dnf install python python-requests
The tools can most easily be installed via git clone and make install
$> git clone http://github.com/intel/pm-graph.git
$> cd pm-graph
$> sudo make install
$> man sleepgraph ; man bootgraph
Setup involves some minor kernel configuration
The following kernel build options are required for all kernels:
CONFIG_DEVMEM=y
CONFIG_PM_DEBUG=y
CONFIG_PM_SLEEP_DEBUG=y
CONFIG_FTRACE=y
CONFIG_FUNCTION_TRACER=y
CONFIG_FUNCTION_GRAPH_TRACER=y
CONFIG_KPROBES=y
CONFIG_KPROBES_ON_FTRACE=y
In kernel 3.15.0, two patches were upstreamed which enable the
v3.0 behavior. These patches allow the tool to read all the
data from trace events instead of from dmesg. You can enable
this behavior on earlier kernels with these patches:
(kernel/pre-3.15/enable_trace_events_suspend_resume.patch)
(kernel/pre-3.15/enable_trace_events_device_pm_callback.patch)
If you're using a kernel older than 3.15.0, the following
additional kernel parameters are required:
(e.g. in file /etc/default/grub)
GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=32M ..."
If you're using a kernel older than 3.11-rc2, the following simple
patch must be applied to enable ftrace data:
in file: kernel/power/suspend.c
in function: int suspend_devices_and_enter(suspend_state_t state)
remove call to "ftrace_stop();"
remove call to "ftrace_start();"
There is a patch which does this for kernel v3.8.0:
(kernel/pre-3.11-rc2/enable_ftrace_in_suspendresume.patch)
------------------------------------------------------------------
| USAGE |
------------------------------------------------------------------
Basic Usage
___________
1) First configure a kernel using the instructions from the previous sections.
Then build, install, and boot with it.
2) Open up a terminal window and execute the mode list command:
%> sudo ./sleepgraph.py -modes
['freeze', 'mem', 'disk']
Execute a test using one of the available power modes, e.g. mem (S3):
%> sudo ./sleepgraph.py -m mem -rtcwake 15
or with a config file
%> sudo ./sleepgraph.py -config config/suspend.cfg
When the system comes back you'll see the script finishing up and
creating the output files in the test subdir. It generates output
files in subdirectory: suspend-mmddyy-HHMMSS. The ftrace file can
be used to regenerate the html timeline with different options
HTML output: <hostname>_<mode>.html
raw dmesg output: <hostname>_<mode>_dmesg.txt
raw ftrace output: <hostname>_<mode>_ftrace.txt
View the html in firefox or chrome.
Dev Mode Usage
______________
Developer mode adds information on low level source calls to the timeline.
The tool sets kprobes on all delay and mutex calls to see which devices
are waiting for something and when. It also sets a suite of kprobes on
subsystem dependent calls to better fill out the timeline.
The tool will also expose kernel threads that don't normally show up in the
timeline. This is useful in discovering dependent threads to get a better
idea of what each device is waiting for. For instance, the scsi_eh thread,
a.k.a. scsi resume error handler, is what each SATA disk device waits for
before it can continue resume.
The timeline will be much larger if run with dev mode, so it can be useful
to set the -mindev option to clip out any device blocks that are too small
to see easily. The following command will give a nice dev mode run:
%> sudo ./sleepgraph.py -m mem -rtcwake 15 -mindev 1 -dev
or with a config file
%> sudo ./sleepgraph.py -config config/suspend-dev.cfg
Proc Mode Usage
_______________
Proc mode adds user process info to the timeline. This is done in a manner
similar to the bootchart utility, which graphs init processes and their
execution as the system boots. This tool option does the same thing but for
the period before and after suspend/resume.
In order to see any process info, there needs to be some delay before or
after resume since processes are frozen in suspend_prepare and thawed in
resume_complete. The predelay and postdelay args allow you to do this. It
can also be useful to run in x2 mode with an x2 delay, this way you can
see process activity before and after resume, and in between two
successive suspend/resumes.
The command can be run like this:
%> sudo ./sleepgraph.py -m mem -rtcwake 15 -x2 -x2delay 1000 -predelay 1000 -postdelay 1000 -proc
or with a config file
%> sudo ./sleepgraph.py -config config/suspend-proc.cfg
------------------------------------------------------------------
| CONFIGURATION FILES |
------------------------------------------------------------------
Since 4.0 we've moved to using config files in lieu of command line options.
The config folder contains a collection of typical use cases.
There are corresponding configs for other power modes:
Simple suspend/resume with basic timeline (mem/freeze/standby)
config/suspend.cfg
config/freeze.cfg
config/standby.cfg
Dev mode suspend/resume with dev timeline (mem/freeze/standby)
config/suspend-dev.cfg
config/freeze-dev.cfg
config/standby-dev.cfg
Simple suspend/resume with timeline and callgraph (mem/freeze/standby)
config/suspend-callgraph.cfg
config/freeze-callgraph.cfg
config/standby-callgraph.cfg
Sample proc mode x2 run using mem suspend
config/suspend-x2-proc.cfg
Sample for editing timeline funcs (moves internal functions into config)
config/custom-timeline-functions.cfg
Sample debug config for serio subsystem
config/debug-serio-suspend.cfg
Usage Examples
______________
Run a simple mem suspend:
%> sudo ./sleepgraph.py -config config/suspend.cfg
Run a mem suspend with callgraph data:
%> sudo ./sleepgraph.py -config config/suspend-callgraph.cfg
Run a mem suspend with dev mode detail:
%> sudo ./sleepgraph.py -config config/suspend-dev.cfg
Config File Options
___________________
[Settings]
# Verbosity: print verbose messages (def: false)
verbose: false
# Suspend Mode: e.g. standby, mem, freeze, disk (def: mem)
mode: mem
# Output Directory Format: {hostname}, {date}, {time} give current values
output-dir: suspend-{hostname}-{date}-{time}
# Automatic Wakeup: use rtcwake to wakeup after X seconds (def: infinity)
rtcwake: 15
# Add Logs: add the dmesg and ftrace log to the html output (def: false)
addlogs: false
# Sus/Res Gap: insert a gap between sus & res in the timeline (def: false)
srgap: false
# Custom Command: Command to execute in lieu of suspend (def: "")
command: echo mem > /sys/power/state
# Proc mode: graph user processes and cpu usage in the timeline (def: false)
proc: false
# Dev mode: graph source functions in the timeline (def: false)
dev: false
# Suspend/Resume x2: run 2 suspend/resumes back to back (def: false)
x2: false
# x2 Suspend Delay: time delay between the two test runs in ms (def: 0 ms)
x2delay: 0
# Pre Suspend Delay: nclude an N ms delay before (1st) suspend (def: 0 ms)
predelay: 0
# Post Resume Delay: include an N ms delay after (last) resume (def: 0 ms)
postdelay: 0
# Min Device Length: graph only dev callbacks longer than min (def: 0.001 ms)
mindev: 0.001
# Callgraph: gather ftrace callgraph data on all timeline events (def: false)
callgraph: false
# Expand Callgraph: pre-expand the callgraph treeviews in html (def: false)
expandcg: false
# Min Callgraph Length: show callgraphs only if longer than min (def: 1 ms)
mincg: 1
# Timestamp Precision: number of sig digits in timestamps (0:S, [3:ms], 6:us)
timeprec: 3
# Device Filter: show only devs whose name/driver includes one of these strings
devicefilter: _cpu_up,_cpu_down,i915,usb
# Override default timeline entries:
# Do not use the internal default functions for timeline entries (def: false)
# Set this to true if you intend to only use the ones defined in the config
override-timeline-functions: true
# Override default dev timeline entries:
# Do not use the internal default functions for dev timeline entries (def: false)
# Set this to true if you intend to only use the ones defined in the config
override-dev-timeline-functions: true
# Call Loop Max Gap (dev mode only)
# merge loops of the same call if each is less than maxgap apart (def: 100us)
callloop-maxgap: 0.0001
# Call Loop Max Length (dev mode only)
# merge loops of the same call if each is less than maxlen in length (def: 5ms)
callloop-maxlen: 0.005
------------------------------------------------------------------
| CUSTOM TIMELINE ENTRIES |
------------------------------------------------------------------
Adding or Editing Timeline Functions
____________________________________
The tool uses an array of function names to fill out empty spaces in the
timeline where device callbacks don't appear. For instance, in suspend_prepare
the tool adds the sys_sync and freeze_processes calls as virtual device blocks
in the timeline to show you where the time is going. These calls should fill
the timeline with contiguous data so that most kernel execution is covered.
It is possible to add new function calls to the timeline by adding them to
the config. It's also possible to copy the internal timeline functions into
the config so that you can override and edit them. Place them in the
timeline_functions_ARCH section with the name of your architecture appended.
i.e. for x86_64: [timeline_functions_x86_64]
Use the override-timeline-functions option if you only want to use your
custom calls, or leave it false to append them to the internal ones.
This section includes a list of functions (set using kprobes) which use both
symbol data and function arg data. The args are pulled directly from the
stack using this architecture's registers and stack formatting. Each entry
can include up to four pieces of info: The function name, a format string,
an argument list, and a color. But only a function name is required.
For a full example config, see config/custom-timeline-functions.cfg. It pulls
all the internal timeline functions into the config and allows you to edit
them.
Entry format:
function: format{fn_arg1}_{fn_arg2} fn_arg1 fn_arg2 ... [color=purple]
Required Arguments:
function: The symbol name for the function you want probed, this is the
minimum required for an entry, it will show up as the function
name with no arguments.
example: _cpu_up:
Optional Arguments:
format: The format to display the data on the timeline in. Use braces to
enclose the arg names.
example: CPU_ON[{cpu}]
color: The color of the entry block in the timeline. The default color is
transparent, so the entry shares the phase color. The color is an
html color string, either a word, or an RGB.
example: [color=#CC00CC]
arglist: A list of arguments from registers/stack addresses. See URL:
https://www.kernel.org/doc/Documentation/trace/kprobetrace.txt
example: cpu=%di:s32
Here is a full example entry. It displays cpu resume calls in the timeline
in orange. They will appear as CPU_ON[0], CPU_ON[1], etc.
[timeline_functions_x86_64]
_cpu_up: CPU_ON[{cpu}] cpu=%di:s32 [color=orange]
Adding or Editing Dev Mode Timeline Source Functions
____________________________________________________
In dev mode, the tool uses an array of function names to monitor source
execution within the timeline entries.
The function calls are displayed inside the main device/call blocks in the
timeline. However, if a function call is not within a main timeline event,
it will spawn an entirely new event named after the caller's kernel thread.
These asynchronous kernel threads will populate in a separate section
beneath the main device/call section.
The tool has a set of hard coded calls which focus on the most common use
cases: msleep, udelay, schedule_timeout, mutex_lock_slowpath, etc. These are
the functions that add a hardcoded time delay to the suspend/resume path.
The tool also includes some common functions native to important
subsystems: ata, i915, and ACPI, etc.
It is possible to add new function calls to the dev timeline by adding them
to the config. It's also possible to copy the internal dev timeline
functions into the config so that you can override and edit them. Place them
in the dev_timeline_functions_ARCH section with the name of your architecture
appended. i.e. for x86_64: [dev_timeline_functions_x86_64]
Use the override-dev-timeline-functions option if you only want to use your
custom calls, or leave it false to append them to the internal ones.
The format is the same as the timeline_functions_x86_64 section. It's a
list of functions (set using kprobes) which use both symbol data and function
arg data. The args are pulled directly from the stack using this
architecture's registers and stack formatting. Each entry can include up
to four pieces of info: The function name, a format string, an argument list,
and a color. But only the function name is required.
For a full example config, see config/custom-timeline-functions.cfg. It pulls
all the internal dev timeline functions into the config and allows you to edit
them.
Here is a full example entry. It displays the ATA port reset calls as
ataN_port_reset in the timeline. This is where most of the SATA disk resume
time goes, so it can be helpful to see the low level call.
[dev_timeline_functions_x86_64]
ata_eh_recover: ata{port}_port_reset port=+36(%di):s32 [color=#CC00CC]
Verifying your custom functions
_______________________________
Once you have a set of functions (kprobes) defined, it can be useful to
perform a quick check to see if you formatted them correctly and if the system
actually supports them. To do this, run the tool with your config file
and the -status option. The tool will go through all the kprobes (both
custom and internal if you haven't overridden them) and actually attempts
to set them in ftrace. It will then print out success or fail for you.
Note that kprobes which don't actually exist in the kernel won't stop the
tool, they just wont show up.
For example:
sudo ./sleepgraph.py -config config/custom-timeline-functions.cfg -status
Checking this system (myhostname)...
have root access: YES
is sysfs mounted: YES
is "mem" a valid power mode: YES
is ftrace supported: YES
are kprobes supported: YES
timeline data source: FTRACE (all trace events found)
is rtcwake supported: YES
verifying timeline kprobes work:
_cpu_down: YES
_cpu_up: YES
acpi_pm_finish: YES
acpi_pm_prepare: YES
freeze_kernel_threads: YES
freeze_processes: YES
sys_sync: YES
thaw_processes: YES
verifying dev kprobes work:
__const_udelay: YES
__mutex_lock_slowpath: YES
acpi_os_stall: YES
acpi_ps_parse_aml: YES
intel_opregion_init: NO
intel_opregion_register: NO
intel_opregion_setup: NO
msleep: YES
schedule_timeout: YES
schedule_timeout_uninterruptible: YES
usleep_range: YES
------------------------------------------------------------------
| TESTING ON CONSUMER LINUX OPERATING SYSTEMS |
------------------------------------------------------------------
Android
_______
The easiest way to execute on an android device is to run the android.sh
script on the device, then pull the ftrace log back to the host and run
sleepgraph.py on it.
Here are the steps:
[download and install the tool on the device]
host%> wget https://raw.githubusercontent.com/intel/pm-graph/master/tools/android.sh
host%> adb connect 192.168.1.6
host%> adb root
# push the script to a writeable location
host%> adb push android.sh /sdcard/
[check whether the tool will run on your device]
host%> adb shell
dev%> cd /sdcard
dev%> sh android.sh status
host : asus_t100
kernel : 3.14.0-i386-dirty
modes : freeze mem
rtcwake : supported
ftrace : supported
trace events {
suspend_resume: found
device_pm_callback_end: found
device_pm_callback_start: found
}
# the above is what you see on a system that's properly patched
[execute the suspend]
# NOTE: The suspend will only work if the screen isn't timed out,
# so you have to press some keys first to wake it up b4 suspend)
dev%> sh android.sh suspend mem
------------------------------------
Suspend/Resume timing test initiated
------------------------------------
hostname : asus_t100
kernel : 3.14.0-i386-dirty
mode : mem
ftrace out : /mnt/shell/emulated/0/ftrace.txt
dmesg out : /mnt/shell/emulated/0/dmesg.txt
log file : /mnt/shell/emulated/0/log.txt
------------------------------------
INITIALIZING FTRACE........DONE
STARTING FTRACE
SUSPEND START @ 21:24:02 (rtcwake in 10 seconds)
<adb connection will now terminate>
[retrieve the data from the device]
# I find that you have to actually kill the adb process and
# reconnect sometimes in order for the connection to work post-suspend
host%> adb connect 192.168.1.6
# (required) get the ftrace data, this is the most important piece
host%> adb pull /sdcard/ftrace.txt
# (optional) get the dmesg data, this is for debugging
host%> adb pull /sdcard/dmesg.txt
# (optional) get the log, which just lists some test times for comparison
host%> adb pull /sdcard/log.txt
[create an output html file using sleepgraph.py]
host%> sleepgraph.py -ftrace ftrace.txt
You should now have an output.html with the android data, enjoy!
...@@ -325,9 +325,9 @@ def parseKernelLog(): ...@@ -325,9 +325,9 @@ def parseKernelLog():
if(not sysvals.stamp['kernel']): if(not sysvals.stamp['kernel']):
sysvals.stamp['kernel'] = sysvals.kernelVersion(msg) sysvals.stamp['kernel'] = sysvals.kernelVersion(msg)
continue continue
m = re.match('.* setting system clock to (?P<t>.*) UTC.*', msg) m = re.match('.* setting system clock to (?P<d>[0-9\-]*)[ A-Z](?P<t>[0-9:]*) UTC.*', msg)
if(m): if(m):
bt = datetime.strptime(m.group('t'), '%Y-%m-%d %H:%M:%S') bt = datetime.strptime(m.group('d')+' '+m.group('t'), '%Y-%m-%d %H:%M:%S')
bt = bt - timedelta(seconds=int(ktime)) bt = bt - timedelta(seconds=int(ktime))
data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S') data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S')
sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p') sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p')
...@@ -348,7 +348,7 @@ def parseKernelLog(): ...@@ -348,7 +348,7 @@ def parseKernelLog():
data.newAction(phase, f, pid, start, ktime, int(r), int(t)) data.newAction(phase, f, pid, start, ktime, int(r), int(t))
del devtemp[f] del devtemp[f]
continue continue
if(re.match('^Freeing unused kernel memory.*', msg)): if(re.match('^Freeing unused kernel .*', msg)):
data.tUserMode = ktime data.tUserMode = ktime
data.dmesg['kernel']['end'] = ktime data.dmesg['kernel']['end'] = ktime
data.dmesg['user']['start'] = ktime data.dmesg['user']['start'] = ktime
...@@ -1008,7 +1008,7 @@ if __name__ == '__main__': ...@@ -1008,7 +1008,7 @@ if __name__ == '__main__':
updateKernelParams() updateKernelParams()
elif cmd == 'flistall': elif cmd == 'flistall':
for f in sysvals.getBootFtraceFilterFunctions(): for f in sysvals.getBootFtraceFilterFunctions():
print f print(f)
elif cmd == 'checkbl': elif cmd == 'checkbl':
sysvals.getBootLoader() sysvals.getBootLoader()
pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec)) pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec))
......
...@@ -98,12 +98,34 @@ postdelay: 0 ...@@ -98,12 +98,34 @@ postdelay: 0
# graph only devices longer than min in the timeline (default: 0.001 ms) # graph only devices longer than min in the timeline (default: 0.001 ms)
mindev: 0.001 mindev: 0.001
# Call Loop Max Gap (dev mode only)
# merge loops of the same call if each is less than maxgap apart (def: 100us)
callloop-maxgap: 0.0001
# Call Loop Max Length (dev mode only)
# merge loops of the same call if each is less than maxlen in length (def: 5ms)
callloop-maxlen: 0.005
# Override default timeline entries:
# Do not use the internal default functions for timeline entries (def: false)
# Set this to true if you intend to only use the ones defined in the config
override-timeline-functions: true
# Override default dev timeline entries:
# Do not use the internal default functions for dev timeline entries (def: false)
# Set this to true if you intend to only use the ones defined in the config
override-dev-timeline-functions: true
# ---- Debug Options ---- # ---- Debug Options ----
# Callgraph # Callgraph
# gather detailed ftrace callgraph data on all timeline events (default: false) # gather detailed ftrace callgraph data on all timeline events (default: false)
callgraph: false callgraph: false
# Max graph depth
# limit the callgraph trace to this depth (default: 0 = all)
maxdepth: 2
# Callgraph phase filter # Callgraph phase filter
# Only enable callgraphs for one phase, i.e. resume_noirq (default: all) # Only enable callgraphs for one phase, i.e. resume_noirq (default: all)
cgphase: suspend cgphase: suspend
...@@ -131,3 +153,7 @@ timeprec: 6 ...@@ -131,3 +153,7 @@ timeprec: 6
# Add kprobe functions to the timeline # Add kprobe functions to the timeline
# Add functions to the timeline from a text file (default: no-action) # Add functions to the timeline from a text file (default: no-action)
# fadd: file.txt # fadd: file.txt
# Ftrace buffer size
# Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB)
# bufsize: 1000
...@@ -53,6 +53,11 @@ disable rtcwake and require a user keypress to resume. ...@@ -53,6 +53,11 @@ disable rtcwake and require a user keypress to resume.
Add the dmesg and ftrace logs to the html output. They will be viewable by Add the dmesg and ftrace logs to the html output. They will be viewable by
clicking buttons in the timeline. clicking buttons in the timeline.
.TP .TP
\fB-turbostat\fR
Use turbostat to execute the command in freeze mode (default: disabled). This
will provide turbostat output in the log which will tell you which actual
power modes were entered.
.TP
\fB-result \fIfile\fR \fB-result \fIfile\fR
Export a results table to a text file for parsing. Export a results table to a text file for parsing.
.TP .TP
...@@ -121,6 +126,10 @@ be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}. ...@@ -121,6 +126,10 @@ be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}.
Use ftrace to create device callgraphs (default: disabled). This can produce Use ftrace to create device callgraphs (default: disabled). This can produce
very large outputs, i.e. 10MB - 100MB. very large outputs, i.e. 10MB - 100MB.
.TP .TP
\fB-ftop\fR
Use ftrace on the top level call: "suspend_devices_and_enter" only (default: disabled).
This option implies -f and creates a single callgraph covering all of suspend/resume.
.TP
\fB-maxdepth \fIlevel\fR \fB-maxdepth \fIlevel\fR
limit the callgraph trace depth to \fIlevel\fR (default: 0=all). This is limit the callgraph trace depth to \fIlevel\fR (default: 0=all). This is
the best way to limit the output size when using callgraphs via -f. the best way to limit the output size when using callgraphs via -f.
...@@ -138,8 +147,8 @@ which are barely visible in the timeline. ...@@ -138,8 +147,8 @@ which are barely visible in the timeline.
The value is a float: e.g. 0.001 represents 1 us. The value is a float: e.g. 0.001 represents 1 us.
.TP .TP
\fB-cgfilter \fI"func1,func2,..."\fR \fB-cgfilter \fI"func1,func2,..."\fR
Reduce callgraph output in the timeline by limiting it to a list of calls. The Reduce callgraph output in the timeline by limiting it certain devices. The
argument can be a single function name or a comma delimited list. argument can be a single device name or a comma delimited list.
(default: none) (default: none)
.TP .TP
\fB-cgskip \fIfile\fR \fB-cgskip \fIfile\fR
...@@ -183,6 +192,9 @@ Print out the contents of the ACPI Firmware Performance Data Table. ...@@ -183,6 +192,9 @@ Print out the contents of the ACPI Firmware Performance Data Table.
\fB-battery\fR \fB-battery\fR
Print out battery status and current charge. Print out battery status and current charge.
.TP .TP
\fB-wifi\fR
Print out wifi status and connection details.
.TP
\fB-xon/-xoff/-xstandby/-xsuspend\fR \fB-xon/-xoff/-xstandby/-xsuspend\fR
Test xset by attempting to switch the display to the given mode. This Test xset by attempting to switch the display to the given mode. This
is the same command which will be issued by \fB-display \fImode\fR. is the same command which will be issued by \fB-display \fImode\fR.
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
# #
# Links: # Links:
# Home Page # Home Page
# https://01.org/suspendresume # https://01.org/pm-graph
# Source repo # Source repo
# git@github.com:01org/pm-graph # git@github.com:intel/pm-graph
# #
# Description: # Description:
# This tool is designed to assist kernel and OS developers in optimizing # This tool is designed to assist kernel and OS developers in optimizing
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
# viewed in firefox or chrome. # viewed in firefox or chrome.
# #
# The following kernel build options are required: # The following kernel build options are required:
# CONFIG_DEVMEM=y
# CONFIG_PM_DEBUG=y # CONFIG_PM_DEBUG=y
# CONFIG_PM_SLEEP_DEBUG=y # CONFIG_PM_SLEEP_DEBUG=y
# CONFIG_FTRACE=y # CONFIG_FTRACE=y
...@@ -53,6 +54,7 @@ import ConfigParser ...@@ -53,6 +54,7 @@ import ConfigParser
import gzip import gzip
from threading import Thread from threading import Thread
from subprocess import call, Popen, PIPE from subprocess import call, Popen, PIPE
import base64
def pprint(msg): def pprint(msg):
print(msg) print(msg)
...@@ -66,7 +68,7 @@ def pprint(msg): ...@@ -66,7 +68,7 @@ def pprint(msg):
# store system values and test parameters # store system values and test parameters
class SystemValues: class SystemValues:
title = 'SleepGraph' title = 'SleepGraph'
version = '5.2' version = '5.4'
ansi = False ansi = False
rs = 0 rs = 0
display = '' display = ''
...@@ -74,8 +76,9 @@ class SystemValues: ...@@ -74,8 +76,9 @@ class SystemValues:
sync = False sync = False
verbose = False verbose = False
testlog = True testlog = True
dmesglog = False dmesglog = True
ftracelog = False ftracelog = False
tstat = False
mindevlen = 0.0 mindevlen = 0.0
mincglen = 0.0 mincglen = 0.0
cgphase = '' cgphase = ''
...@@ -99,6 +102,8 @@ class SystemValues: ...@@ -99,6 +102,8 @@ class SystemValues:
pmdpath = '/sys/power/pm_debug_messages' pmdpath = '/sys/power/pm_debug_messages'
traceevents = [ traceevents = [
'suspend_resume', 'suspend_resume',
'wakeup_source_activate',
'wakeup_source_deactivate',
'device_pm_callback_end', 'device_pm_callback_end',
'device_pm_callback_start' 'device_pm_callback_start'
] ]
...@@ -130,6 +135,8 @@ class SystemValues: ...@@ -130,6 +135,8 @@ class SystemValues:
x2delay = 0 x2delay = 0
skiphtml = False skiphtml = False
usecallgraph = False usecallgraph = False
ftopfunc = 'suspend_devices_and_enter'
ftop = False
usetraceevents = False usetraceevents = False
usetracemarkers = True usetracemarkers = True
usekprobes = True usekprobes = True
...@@ -158,6 +165,13 @@ class SystemValues: ...@@ -158,6 +165,13 @@ class SystemValues:
'acpi_hibernation_leave': {}, 'acpi_hibernation_leave': {},
'acpi_pm_freeze': {}, 'acpi_pm_freeze': {},
'acpi_pm_thaw': {}, 'acpi_pm_thaw': {},
'acpi_s2idle_end': {},
'acpi_s2idle_sync': {},
'acpi_s2idle_begin': {},
'acpi_s2idle_prepare': {},
'acpi_s2idle_wake': {},
'acpi_s2idle_wakeup': {},
'acpi_s2idle_restore': {},
'hibernate_preallocate_memory': {}, 'hibernate_preallocate_memory': {},
'create_basic_memory_bitmaps': {}, 'create_basic_memory_bitmaps': {},
'swsusp_write': {}, 'swsusp_write': {},
...@@ -191,9 +205,14 @@ class SystemValues: ...@@ -191,9 +205,14 @@ class SystemValues:
'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 }, 'usleep_range': { 'args_x86_64': {'min':'%di:s32', 'max':'%si:s32'}, 'ub': 1 },
'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 }, 'mutex_lock_slowpath': { 'func':'__mutex_lock_slowpath', 'ub': 1 },
'acpi_os_stall': {'ub': 1}, 'acpi_os_stall': {'ub': 1},
'rt_mutex_slowlock': {'ub': 1},
# ACPI # ACPI
'acpi_resume_power_resources': {}, 'acpi_resume_power_resources': {},
'acpi_ps_parse_aml': {}, 'acpi_ps_execute_method': { 'args_x86_64': {
'fullpath':'+0(+40(%di)):string',
}},
# mei_me
'mei_reset': {},
# filesystem # filesystem
'ext4_sync_fs': {}, 'ext4_sync_fs': {},
# 80211 # 80211
...@@ -242,6 +261,7 @@ class SystemValues: ...@@ -242,6 +261,7 @@ class SystemValues:
timeformat = '%.3f' timeformat = '%.3f'
cmdline = '%s %s' % \ cmdline = '%s %s' % \
(os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:])) (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
kparams = ''
sudouser = '' sudouser = ''
def __init__(self): def __init__(self):
self.archargs = 'args_'+platform.machine() self.archargs = 'args_'+platform.machine()
...@@ -320,6 +340,7 @@ class SystemValues: ...@@ -320,6 +340,7 @@ class SystemValues:
args['date'] = n.strftime('%y%m%d') args['date'] = n.strftime('%y%m%d')
args['time'] = n.strftime('%H%M%S') args['time'] = n.strftime('%H%M%S')
args['hostname'] = args['host'] = self.hostname args['hostname'] = args['host'] = self.hostname
args['mode'] = self.suspendmode
return value.format(**args) return value.format(**args)
def setOutputFile(self): def setOutputFile(self):
if self.dmesgfile != '': if self.dmesgfile != '':
...@@ -331,21 +352,28 @@ class SystemValues: ...@@ -331,21 +352,28 @@ class SystemValues:
if(m): if(m):
self.htmlfile = m.group('name')+'.html' self.htmlfile = m.group('name')+'.html'
def systemInfo(self, info): def systemInfo(self, info):
p = c = m = b = '' p = m = ''
if 'baseboard-manufacturer' in info: if 'baseboard-manufacturer' in info:
m = info['baseboard-manufacturer'] m = info['baseboard-manufacturer']
elif 'system-manufacturer' in info: elif 'system-manufacturer' in info:
m = info['system-manufacturer'] m = info['system-manufacturer']
if 'baseboard-product-name' in info: if 'system-product-name' in info:
p = info['baseboard-product-name']
elif 'system-product-name' in info:
p = info['system-product-name'] p = info['system-product-name']
if 'processor-version' in info: elif 'baseboard-product-name' in info:
c = info['processor-version'] p = info['baseboard-product-name']
if 'bios-version' in info: if m[:5].lower() == 'intel' and 'baseboard-product-name' in info:
b = info['bios-version'] p = info['baseboard-product-name']
self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | numcpu:%d | memsz:%d | memfr:%d' % \ c = info['processor-version'] if 'processor-version' in info else ''
(m, p, c, b, self.cpucount, self.memtotal, self.memfree) b = info['bios-version'] if 'bios-version' in info else ''
r = info['bios-release-date'] if 'bios-release-date' in info else ''
self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
(m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
try:
kcmd = open('/proc/cmdline', 'r').read().strip()
except:
kcmd = ''
if kcmd:
self.sysstamp += '\n# kparams | %s' % kcmd
def printSystemInfo(self, fatal=False): def printSystemInfo(self, fatal=False):
self.rootCheck(True) self.rootCheck(True)
out = dmidecode(self.mempath, fatal) out = dmidecode(self.mempath, fatal)
...@@ -353,10 +381,10 @@ class SystemValues: ...@@ -353,10 +381,10 @@ class SystemValues:
return return
fmt = '%-24s: %s' fmt = '%-24s: %s'
for name in sorted(out): for name in sorted(out):
print fmt % (name, out[name]) print(fmt % (name, out[name]))
print fmt % ('cpucount', ('%d' % self.cpucount)) print(fmt % ('cpucount', ('%d' % self.cpucount)))
print fmt % ('memtotal', ('%d kB' % self.memtotal)) print(fmt % ('memtotal', ('%d kB' % self.memtotal)))
print fmt % ('memfree', ('%d kB' % self.memfree)) print(fmt % ('memfree', ('%d kB' % self.memfree)))
def cpuInfo(self): def cpuInfo(self):
self.cpucount = 0 self.cpucount = 0
fp = open('/proc/cpuinfo', 'r') fp = open('/proc/cpuinfo', 'r')
...@@ -376,7 +404,7 @@ class SystemValues: ...@@ -376,7 +404,7 @@ class SystemValues:
def initTestOutput(self, name): def initTestOutput(self, name):
self.prefix = self.hostname self.prefix = self.hostname
v = open('/proc/version', 'r').read().strip() v = open('/proc/version', 'r').read().strip()
kver = string.split(v)[2] kver = v.split()[2]
fmt = name+'-%m%d%y-%H%M%S' fmt = name+'-%m%d%y-%H%M%S'
testtime = datetime.now().strftime(fmt) testtime = datetime.now().strftime(fmt)
self.teststamp = \ self.teststamp = \
...@@ -391,7 +419,7 @@ class SystemValues: ...@@ -391,7 +419,7 @@ class SystemValues:
self.htmlfile = \ self.htmlfile = \
self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html' self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
if not os.path.isdir(self.testdir): if not os.path.isdir(self.testdir):
os.mkdir(self.testdir) os.makedirs(self.testdir)
def getValueList(self, value): def getValueList(self, value):
out = [] out = []
for i in value.split(','): for i in value.split(','):
...@@ -402,6 +430,12 @@ class SystemValues: ...@@ -402,6 +430,12 @@ class SystemValues:
self.devicefilter = self.getValueList(value) self.devicefilter = self.getValueList(value)
def setCallgraphFilter(self, value): def setCallgraphFilter(self, value):
self.cgfilter = self.getValueList(value) self.cgfilter = self.getValueList(value)
def skipKprobes(self, value):
for k in self.getValueList(value):
if k in self.tracefuncs:
del self.tracefuncs[k]
if k in self.dev_tracefuncs:
del self.dev_tracefuncs[k]
def setCallgraphBlacklist(self, file): def setCallgraphBlacklist(self, file):
self.cgblacklist = self.listFromFile(file) self.cgblacklist = self.listFromFile(file)
def rtcWakeAlarmOn(self): def rtcWakeAlarmOn(self):
...@@ -471,9 +505,9 @@ class SystemValues: ...@@ -471,9 +505,9 @@ class SystemValues:
if 'func' in self.tracefuncs[i]: if 'func' in self.tracefuncs[i]:
i = self.tracefuncs[i]['func'] i = self.tracefuncs[i]['func']
if i in master: if i in master:
print i print(i)
else: else:
print self.colorText(i) print(self.colorText(i))
def setFtraceFilterFunctions(self, list): def setFtraceFilterFunctions(self, list):
master = self.listFromFile(self.tpath+'available_filter_functions') master = self.listFromFile(self.tpath+'available_filter_functions')
flist = '' flist = ''
...@@ -680,7 +714,8 @@ class SystemValues: ...@@ -680,7 +714,8 @@ class SystemValues:
if self.bufsize > 0: if self.bufsize > 0:
tgtsize = self.bufsize tgtsize = self.bufsize
elif self.usecallgraph or self.usedevsrc: elif self.usecallgraph or self.usedevsrc:
bmax = (1*1024*1024) if self.suspendmode == 'disk' else (3*1024*1024) bmax = (1*1024*1024) if self.suspendmode in ['disk', 'command'] \
else (3*1024*1024)
tgtsize = min(self.memfree, bmax) tgtsize = min(self.memfree, bmax)
else: else:
tgtsize = 65536 tgtsize = 65536
...@@ -715,6 +750,9 @@ class SystemValues: ...@@ -715,6 +750,9 @@ class SystemValues:
cf.append(self.tracefuncs[fn]['func']) cf.append(self.tracefuncs[fn]['func'])
else: else:
cf.append(fn) cf.append(fn)
if self.ftop:
self.setFtraceFilterFunctions([self.ftopfunc])
else:
self.setFtraceFilterFunctions(cf) self.setFtraceFilterFunctions(cf)
# initialize the kprobe trace # initialize the kprobe trace
elif self.usekprobes: elif self.usekprobes:
...@@ -768,9 +806,21 @@ class SystemValues: ...@@ -768,9 +806,21 @@ class SystemValues:
fw = test['fw'] fw = test['fw']
if(fw): if(fw):
fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1])) fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
if 'mcelog' in test:
fp.write('# mcelog %s\n' % test['mcelog'])
if 'turbo' in test:
fp.write('# turbostat %s\n' % test['turbo'])
if 'bat' in test: if 'bat' in test:
(a1, c1), (a2, c2) = test['bat'] (a1, c1), (a2, c2) = test['bat']
fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2)) fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2))
if 'wifi' in test:
wstr = []
for wifi in test['wifi']:
tmp = []
for key in sorted(wifi):
tmp.append('%s:%s' % (key, wifi[key]))
wstr.append('|'.join(tmp))
fp.write('# wifi %s\n' % (','.join(wstr)))
if test['error'] or len(testdata) > 1: if test['error'] or len(testdata) > 1:
fp.write('# enter_sleep_error %s\n' % test['error']) fp.write('# enter_sleep_error %s\n' % test['error'])
return fp return fp
...@@ -821,6 +871,106 @@ class SystemValues: ...@@ -821,6 +871,106 @@ class SystemValues:
if isgz: if isgz:
return gzip.open(filename, mode+'b') return gzip.open(filename, mode+'b')
return open(filename, mode) return open(filename, mode)
def mcelog(self, clear=False):
cmd = self.getExec('mcelog')
if not cmd:
return ''
if clear:
call(cmd+' > /dev/null 2>&1', shell=True)
return ''
fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
out = fp.read().strip()
fp.close()
if not out:
return ''
return base64.b64encode(out.encode('zlib'))
def haveTurbostat(self):
if not self.tstat:
return False
cmd = self.getExec('turbostat')
if not cmd:
return False
fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
out = fp.read().strip()
fp.close()
return re.match('turbostat version [0-9\.]* .*', out)
def turbostat(self):
cmd = self.getExec('turbostat')
if not cmd:
return 'missing turbostat executable'
text = []
fullcmd = '%s -q -S echo freeze > %s' % (cmd, self.powerfile)
fp = Popen(['sh', '-c', fullcmd], stdout=PIPE, stderr=PIPE).stderr
for line in fp:
if re.match('[0-9.]* sec', line):
continue
text.append(line.split())
fp.close()
if len(text) < 2:
return 'turbostat output format error'
out = []
for key in text[0]:
values = []
idx = text[0].index(key)
for line in text[1:]:
if len(line) > idx:
values.append(line[idx])
out.append('%s=%s' % (key, ','.join(values)))
return '|'.join(out)
def checkWifi(self):
out = dict()
iwcmd, ifcmd = self.getExec('iwconfig'), self.getExec('ifconfig')
if not iwcmd or not ifcmd:
return out
fp = Popen(iwcmd, stdout=PIPE, stderr=PIPE).stdout
for line in fp:
m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', line)
if not m:
continue
out['device'] = m.group('dev')
if '"' in m.group('ess'):
out['essid'] = m.group('ess').strip('"')
break
fp.close()
if 'device' in out:
fp = Popen([ifcmd, out['device']], stdout=PIPE, stderr=PIPE).stdout
for line in fp:
m = re.match('.* inet (?P<ip>[0-9\.]*)', line)
if m:
out['ip'] = m.group('ip')
break
fp.close()
return out
def errorSummary(self, errinfo, msg):
found = False
for entry in errinfo:
if re.match(entry['match'], msg):
entry['count'] += 1
if self.hostname not in entry['urls']:
entry['urls'][self.hostname] = [self.htmlfile]
elif self.htmlfile not in entry['urls'][self.hostname]:
entry['urls'][self.hostname].append(self.htmlfile)
found = True
break
if found:
return
arr = msg.split()
for j in range(len(arr)):
if re.match('^[0-9,\-\.]*$', arr[j]):
arr[j] = '[0-9,\-\.]*'
else:
arr[j] = arr[j]\
.replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
.replace('.', '\.').replace('+', '\+').replace('*', '\*')\
.replace('(', '\(').replace(')', '\)')
mstr = ' '.join(arr)
entry = {
'line': msg,
'match': mstr,
'count': 1,
'urls': {self.hostname: [self.htmlfile]}
}
errinfo.append(entry)
sysvals = SystemValues() sysvals = SystemValues()
switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0'] switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
...@@ -915,7 +1065,14 @@ class Data: ...@@ -915,7 +1065,14 @@ class Data:
'ERROR' : '.*ERROR.*', 'ERROR' : '.*ERROR.*',
'WARNING' : '.*WARNING.*', 'WARNING' : '.*WARNING.*',
'IRQ' : '.*genirq: .*', 'IRQ' : '.*genirq: .*',
'TASKFAIL': '.*Freezing of tasks failed.*', 'TASKFAIL': '.*Freezing of tasks *.*',
'ACPI' : '.*ACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
'DEVFAIL' : '.* failed to (?P<b>[a-z]*) async: .*',
'DISKFULL': '.*No space left on device.*',
'USBERR' : '.*usb .*device .*, error [0-9-]*',
'ATAERR' : ' *ata[0-9\.]*: .*failed.*',
'MEIERR' : ' *mei.*: .*failed.*',
'TPMERR' : '(?i) *tpm *tpm[0-9]*: .*error.*',
} }
def __init__(self, num): def __init__(self, num):
idchar = 'abcdefghij' idchar = 'abcdefghij'
...@@ -933,6 +1090,9 @@ class Data: ...@@ -933,6 +1090,9 @@ class Data:
self.outfile = '' self.outfile = ''
self.kerror = False self.kerror = False
self.battery = 0 self.battery = 0
self.wifi = 0
self.turbostat = 0
self.mcelog = 0
self.enterfail = '' self.enterfail = ''
self.currphase = '' self.currphase = ''
self.pstl = dict() # process timeline self.pstl = dict() # process timeline
...@@ -967,7 +1127,23 @@ class Data: ...@@ -967,7 +1127,23 @@ class Data:
if len(plist) < 1: if len(plist) < 1:
return '' return ''
return plist[-1] return plist[-1]
def turbostatInfo(self):
tp = TestProps()
out = {'syslpi':'N/A','pkgpc10':'N/A'}
for line in self.dmesgtext:
m = re.match(tp.tstatfmt, line)
if not m:
continue
for i in m.group('t').split('|'):
if 'SYS%LPI' in i:
out['syslpi'] = i.split('=')[-1]+'%'
elif 'pc10' in i:
out['pkgpc10'] = i.split('=')[-1]+'%'
break
return out
def extractErrorInfo(self): def extractErrorInfo(self):
lf = self.dmesgtext
if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
lf = sysvals.openlog(sysvals.dmesgfile, 'r') lf = sysvals.openlog(sysvals.dmesgfile, 'r')
i = 0 i = 0
list = [] list = []
...@@ -983,16 +1159,19 @@ class Data: ...@@ -983,16 +1159,19 @@ class Data:
msg = m.group('msg') msg = m.group('msg')
for err in self.errlist: for err in self.errlist:
if re.match(self.errlist[err], msg): if re.match(self.errlist[err], msg):
list.append((err, dir, t, i, i)) list.append((msg, err, dir, t, i, i))
self.kerror = True self.kerror = True
break break
for e in list: msglist = []
type, dir, t, idx1, idx2 = e for msg, type, dir, t, idx1, idx2 in list:
msglist.append(msg)
sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t))
self.errorinfo[dir].append((type, t, idx1, idx2)) self.errorinfo[dir].append((type, t, idx1, idx2))
if self.kerror: if self.kerror:
sysvals.dmesglog = True sysvals.dmesglog = True
if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
lf.close() lf.close()
return msglist
def setStart(self, time): def setStart(self, time):
self.start = time self.start = time
def setEnd(self, time): def setEnd(self, time):
...@@ -2045,7 +2224,7 @@ class FTraceCallGraph: ...@@ -2045,7 +2224,7 @@ class FTraceCallGraph:
if(data.dmesg[p]['start'] <= self.start and if(data.dmesg[p]['start'] <= self.start and
self.start <= data.dmesg[p]['end']): self.start <= data.dmesg[p]['end']):
list = data.dmesg[p]['list'] list = data.dmesg[p]['list']
for devname in list: for devname in sorted(list, key=lambda k:list[k]['start']):
dev = list[devname] dev = list[devname]
if(pid == dev['pid'] and if(pid == dev['pid'] and
self.start <= dev['start'] and self.start <= dev['start'] and
...@@ -2350,6 +2529,9 @@ class TestProps: ...@@ -2350,6 +2529,9 @@ class TestProps:
'(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\ '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$' ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)' batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)'
wififmt = '^# wifi (?P<w>.*)'
tstatfmt = '^# turbostat (?P<t>\S*)'
mcelogfmt = '^# mcelog (?P<m>\S*)'
testerrfmt = '^# enter_sleep_error (?P<e>.*)' testerrfmt = '^# enter_sleep_error (?P<e>.*)'
sysinfofmt = '^# sysinfo .*' sysinfofmt = '^# sysinfo .*'
cmdlinefmt = '^# command \| (?P<cmd>.*)' cmdlinefmt = '^# command \| (?P<cmd>.*)'
...@@ -2372,7 +2554,10 @@ class TestProps: ...@@ -2372,7 +2554,10 @@ class TestProps:
self.cmdline = '' self.cmdline = ''
self.kparams = '' self.kparams = ''
self.testerror = [] self.testerror = []
self.mcelog = []
self.turbostat = []
self.battery = [] self.battery = []
self.wifi = []
self.fwdata = [] self.fwdata = []
self.ftrace_line_fmt = self.ftrace_line_fmt_nop self.ftrace_line_fmt = self.ftrace_line_fmt_nop
self.cgformat = False self.cgformat = False
...@@ -2386,6 +2571,44 @@ class TestProps: ...@@ -2386,6 +2571,44 @@ class TestProps:
self.ftrace_line_fmt = self.ftrace_line_fmt_nop self.ftrace_line_fmt = self.ftrace_line_fmt_nop
else: else:
doError('Invalid tracer format: [%s]' % tracer) doError('Invalid tracer format: [%s]' % tracer)
def decode(self, data):
try:
out = base64.b64decode(data).decode('zlib')
except:
out = data
return out
def stampInfo(self, line):
if re.match(self.stampfmt, line):
self.stamp = line
return True
elif re.match(self.sysinfofmt, line):
self.sysinfo = line
return True
elif re.match(self.kparamsfmt, line):
self.kparams = line
return True
elif re.match(self.cmdlinefmt, line):
self.cmdline = line
return True
elif re.match(self.mcelogfmt, line):
self.mcelog.append(line)
return True
elif re.match(self.tstatfmt, line):
self.turbostat.append(line)
return True
elif re.match(self.batteryfmt, line):
self.battery.append(line)
return True
elif re.match(self.wififmt, line):
self.wifi.append(line)
return True
elif re.match(self.testerrfmt, line):
self.testerror.append(line)
return True
elif re.match(self.firmwarefmt, line):
self.fwdata.append(line)
return True
return False
def parseStamp(self, data, sv): def parseStamp(self, data, sv):
# global test data # global test data
m = re.match(self.stampfmt, self.stamp) m = re.match(self.stampfmt, self.stamp)
...@@ -2428,14 +2651,31 @@ class TestProps: ...@@ -2428,14 +2651,31 @@ class TestProps:
sv.stamp = data.stamp sv.stamp = data.stamp
# firmware data # firmware data
if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber: if sv.suspendmode == 'mem' and len(self.fwdata) > data.testnumber:
data.fwSuspend, data.fwResume = self.fwdata[data.testnumber] m = re.match(self.firmwarefmt, self.fwdata[data.testnumber])
if m:
data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
if(data.fwSuspend > 0 or data.fwResume > 0): if(data.fwSuspend > 0 or data.fwResume > 0):
data.fwValid = True data.fwValid = True
# mcelog data
if len(self.mcelog) > data.testnumber:
m = re.match(self.mcelogfmt, self.mcelog[data.testnumber])
if m:
data.mcelog = self.decode(m.group('m'))
# turbostat data
if len(self.turbostat) > data.testnumber:
m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
if m:
data.turbostat = m.group('t')
# battery data # battery data
if len(self.battery) > data.testnumber: if len(self.battery) > data.testnumber:
m = re.match(self.batteryfmt, self.battery[data.testnumber]) m = re.match(self.batteryfmt, self.battery[data.testnumber])
if m: if m:
data.battery = m.groups() data.battery = m.groups()
# wifi data
if len(self.wifi) > data.testnumber:
m = re.match(self.wififmt, self.wifi[data.testnumber])
if m:
data.wifi = m.group('w')
# sleep mode enter errors # sleep mode enter errors
if len(self.testerror) > data.testnumber: if len(self.testerror) > data.testnumber:
m = re.match(self.testerrfmt, self.testerror[data.testnumber]) m = re.match(self.testerrfmt, self.testerror[data.testnumber])
...@@ -2505,9 +2745,9 @@ class ProcessMonitor: ...@@ -2505,9 +2745,9 @@ class ProcessMonitor:
# Quickly determine if the ftrace log has all of the trace events, # Quickly determine if the ftrace log has all of the trace events,
# markers, and/or kprobes required for primary parsing. # markers, and/or kprobes required for primary parsing.
def doesTraceLogHaveTraceEvents(): def doesTraceLogHaveTraceEvents():
kpcheck = ['_cal: (', '_cpu_down()'] kpcheck = ['_cal: (', '_ret: (']
techeck = ['suspend_resume', 'device_pm_callback'] techeck = ['suspend_resume', 'device_pm_callback']
tmcheck = ['tracing_mark_write'] tmcheck = ['SUSPEND START', 'RESUME COMPLETE']
sysvals.usekprobes = False sysvals.usekprobes = False
fp = sysvals.openlog(sysvals.ftracefile, 'r') fp = sysvals.openlog(sysvals.ftracefile, 'r')
for line in fp: for line in fp:
...@@ -2556,21 +2796,7 @@ def appendIncompleteTraceLog(testruns): ...@@ -2556,21 +2796,7 @@ def appendIncompleteTraceLog(testruns):
for line in tf: for line in tf:
# remove any latent carriage returns # remove any latent carriage returns
line = line.replace('\r\n', '') line = line.replace('\r\n', '')
# grab the stamp and sysinfo if tp.stampInfo(line):
if re.match(tp.stampfmt, line):
tp.stamp = line
continue
elif re.match(tp.sysinfofmt, line):
tp.sysinfo = line
continue
elif re.match(tp.cmdlinefmt, line):
tp.cmdline = line
continue
elif re.match(tp.batteryfmt, line):
tp.battery.append(line)
continue
elif re.match(tp.testerrfmt, line):
tp.testerror.append(line)
continue continue
# determine the trace data type (required for further parsing) # determine the trace data type (required for further parsing)
m = re.match(tp.tracertypefmt, line) m = re.match(tp.tracertypefmt, line)
...@@ -2693,26 +2919,7 @@ def parseTraceLog(live=False): ...@@ -2693,26 +2919,7 @@ def parseTraceLog(live=False):
for line in tf: for line in tf:
# remove any latent carriage returns # remove any latent carriage returns
line = line.replace('\r\n', '') line = line.replace('\r\n', '')
# stamp and sysinfo lines if tp.stampInfo(line):
if re.match(tp.stampfmt, line):
tp.stamp = line
continue
elif re.match(tp.sysinfofmt, line):
tp.sysinfo = line
continue
elif re.match(tp.cmdlinefmt, line):
tp.cmdline = line
continue
elif re.match(tp.batteryfmt, line):
tp.battery.append(line)
continue
elif re.match(tp.testerrfmt, line):
tp.testerror.append(line)
continue
# firmware line: pull out any firmware data
m = re.match(tp.firmwarefmt, line)
if(m):
tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
continue continue
# tracer type line: determine the trace data type # tracer type line: determine the trace data type
m = re.match(tp.tracertypefmt, line) m = re.match(tp.tracertypefmt, line)
...@@ -2925,7 +3132,7 @@ def parseTraceLog(live=False): ...@@ -2925,7 +3132,7 @@ def parseTraceLog(live=False):
tp.ktemp[key].append({ tp.ktemp[key].append({
'pid': pid, 'pid': pid,
'begin': t.time, 'begin': t.time,
'end': t.time, 'end': -1,
'name': displayname, 'name': displayname,
'cdata': kprobedata, 'cdata': kprobedata,
'proc': m_proc, 'proc': m_proc,
...@@ -2936,10 +3143,9 @@ def parseTraceLog(live=False): ...@@ -2936,10 +3143,9 @@ def parseTraceLog(live=False):
elif(t.freturn): elif(t.freturn):
if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1: if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
continue continue
e = tp.ktemp[key][-1] e = next((x for x in reversed(tp.ktemp[key]) if x['end'] < 0), 0)
if e['begin'] < 0.0 or t.time - e['begin'] < 0.000001: if not e:
tp.ktemp[key].pop() continue
else:
e['end'] = t.time e['end'] = t.time
e['rdata'] = kprobedata e['rdata'] = kprobedata
# end of kernel resume # end of kernel resume
...@@ -2963,8 +3169,10 @@ def parseTraceLog(live=False): ...@@ -2963,8 +3169,10 @@ def parseTraceLog(live=False):
if(res == -1): if(res == -1):
testrun.ftemp[key][-1].addLine(t) testrun.ftemp[key][-1].addLine(t)
tf.close() tf.close()
if len(testdata) < 1:
sysvals.vprint('WARNING: ftrace start marker is missing')
if data and not data.devicegroups: if data and not data.devicegroups:
sysvals.vprint('WARNING: end marker is missing') sysvals.vprint('WARNING: ftrace end marker is missing')
data.handleEndMarker(t.time) data.handleEndMarker(t.time)
if sysvals.suspendmode == 'command': if sysvals.suspendmode == 'command':
...@@ -3013,9 +3221,11 @@ def parseTraceLog(live=False): ...@@ -3013,9 +3221,11 @@ def parseTraceLog(live=False):
name, pid = key name, pid = key
if name not in sysvals.tracefuncs: if name not in sysvals.tracefuncs:
continue continue
if pid not in data.devpids:
data.devpids.append(pid)
for e in tp.ktemp[key]: for e in tp.ktemp[key]:
kb, ke = e['begin'], e['end'] kb, ke = e['begin'], e['end']
if kb == ke or tlb > kb or tle <= kb: if ke - kb < 0.000001 or tlb > kb or tle <= kb:
continue continue
color = sysvals.kprobeColor(name) color = sysvals.kprobeColor(name)
data.newActionGlobal(e['name'], kb, ke, pid, color) data.newActionGlobal(e['name'], kb, ke, pid, color)
...@@ -3027,7 +3237,7 @@ def parseTraceLog(live=False): ...@@ -3027,7 +3237,7 @@ def parseTraceLog(live=False):
continue continue
for e in tp.ktemp[key]: for e in tp.ktemp[key]:
kb, ke = e['begin'], e['end'] kb, ke = e['begin'], e['end']
if kb == ke or tlb > kb or tle <= kb: if ke - kb < 0.000001 or tlb > kb or tle <= kb:
continue continue
data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb, data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
ke, e['cdata'], e['rdata']) ke, e['cdata'], e['rdata'])
...@@ -3051,7 +3261,7 @@ def parseTraceLog(live=False): ...@@ -3051,7 +3261,7 @@ def parseTraceLog(live=False):
if not devname: if not devname:
sortkey = '%f%f%d' % (cg.start, cg.end, pid) sortkey = '%f%f%d' % (cg.start, cg.end, pid)
sortlist[sortkey] = cg sortlist[sortkey] = cg
elif len(cg.list) > 1000000: elif len(cg.list) > 1000000 and cg.name != sysvals.ftopfunc:
sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\ sysvals.vprint('WARNING: the callgraph for %s is massive (%d lines)' %\
(devname, len(cg.list))) (devname, len(cg.list)))
# create blocks for orphan cg data # create blocks for orphan cg data
...@@ -3133,25 +3343,7 @@ def loadKernelLog(): ...@@ -3133,25 +3343,7 @@ def loadKernelLog():
idx = line.find('[') idx = line.find('[')
if idx > 1: if idx > 1:
line = line[idx:] line = line[idx:]
# grab the stamp and sysinfo if tp.stampInfo(line):
if re.match(tp.stampfmt, line):
tp.stamp = line
continue
elif re.match(tp.sysinfofmt, line):
tp.sysinfo = line
continue
elif re.match(tp.cmdlinefmt, line):
tp.cmdline = line
continue
elif re.match(tp.batteryfmt, line):
tp.battery.append(line)
continue
elif re.match(tp.testerrfmt, line):
tp.testerror.append(line)
continue
m = re.match(tp.firmwarefmt, line)
if(m):
tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
continue continue
m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
if(not m): if(not m):
...@@ -3176,7 +3368,7 @@ def loadKernelLog(): ...@@ -3176,7 +3368,7 @@ def loadKernelLog():
if data: if data:
testruns.append(data) testruns.append(data)
if len(testruns) < 1: if len(testruns) < 1:
pprint('ERROR: dmesg log has no suspend/resume data: %s' \ doError('dmesg log has no suspend/resume data: %s' \
% sysvals.dmesgfile) % sysvals.dmesgfile)
# fix lines with same timestamp/function with the call and return swapped # fix lines with same timestamp/function with the call and return swapped
...@@ -3515,6 +3707,8 @@ def addCallgraphs(sv, hf, data): ...@@ -3515,6 +3707,8 @@ def addCallgraphs(sv, hf, data):
name += ' '+p name += ' '+p
if('ftrace' in dev): if('ftrace' in dev):
cg = dev['ftrace'] cg = dev['ftrace']
if cg.name == sv.ftopfunc:
name = 'top level suspend/resume call'
num = callgraphHTML(sv, hf, num, cg, num = callgraphHTML(sv, hf, num, cg,
name, color, dev['id']) name, color, dev['id'])
if('ftraces' in dev): if('ftraces' in dev):
...@@ -3523,22 +3717,16 @@ def addCallgraphs(sv, hf, data): ...@@ -3523,22 +3717,16 @@ def addCallgraphs(sv, hf, data):
name+' &rarr; '+cg.name, color, dev['id']) name+' &rarr; '+cg.name, color, dev['id'])
hf.write('\n\n </section>\n') hf.write('\n\n </section>\n')
# Function: createHTMLSummarySimple def summaryCSS(title, center=True):
# Description: tdcenter = 'text-align:center;' if center else ''
# Create summary html file for a series of tests out = '<!DOCTYPE html>\n<html>\n<head>\n\
# Arguments:
# testruns: array of Data objects from parseTraceLog
def createHTMLSummarySimple(testruns, htmlfile, title):
# write the html header first (html head, css code, up to body start)
html = '<!DOCTYPE html>\n<html>\n<head>\n\
<meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\ <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
<title>SleepGraph Summary</title>\n\ <title>'+title+'</title>\n\
<style type=\'text/css\'>\n\ <style type=\'text/css\'>\n\
.stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\ .stamp {width: 100%;text-align:center;background:#888;line-height:30px;color:white;font: 25px Arial;}\n\
table {width:100%;border-collapse: collapse;}\n\ table {width:100%;border-collapse: collapse;border:1px solid;}\n\
.summary {border:1px solid;}\n\
th {border: 1px solid black;background:#222;color:white;}\n\ th {border: 1px solid black;background:#222;color:white;}\n\
td {font: 14px "Times New Roman";text-align: center;}\n\ td {font: 14px "Times New Roman";'+tdcenter+'}\n\
tr.head td {border: 1px solid black;background:#aaa;}\n\ tr.head td {border: 1px solid black;background:#aaa;}\n\
tr.alt {background-color:#ddd;}\n\ tr.alt {background-color:#ddd;}\n\
tr.notice {color:red;}\n\ tr.notice {color:red;}\n\
...@@ -3547,12 +3735,23 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3547,12 +3735,23 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
.maxval {background-color:#FFBBBB;}\n\ .maxval {background-color:#FFBBBB;}\n\
.head a {color:#000;text-decoration: none;}\n\ .head a {color:#000;text-decoration: none;}\n\
</style>\n</head>\n<body>\n' </style>\n</head>\n<body>\n'
return out
# Function: createHTMLSummarySimple
# Description:
# Create summary html file for a series of tests
# Arguments:
# testruns: array of Data objects from parseTraceLog
def createHTMLSummarySimple(testruns, htmlfile, title):
# write the html header first (html head, css code, up to body start)
html = summaryCSS('Summary - SleepGraph')
# extract the test data into list # extract the test data into list
list = dict() list = dict()
tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
num = 0 num = 0
useturbo = False
lastmode = '' lastmode = ''
cnt = dict() cnt = dict()
for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
...@@ -3563,27 +3762,35 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3563,27 +3762,35 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
for i in range(2): for i in range(2):
s = sorted(tMed[i]) s = sorted(tMed[i])
list[lastmode]['med'][i] = s[int(len(s)/2)] list[lastmode]['med'][i] = s[int(len(s)/2)]
iMed[i] = tMed[i].index(list[lastmode]['med'][i]) iMed[i] = tMed[i][list[lastmode]['med'][i]]
list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
list[lastmode]['min'] = tMin list[lastmode]['min'] = tMin
list[lastmode]['max'] = tMax list[lastmode]['max'] = tMax
list[lastmode]['idx'] = (iMin, iMed, iMax) list[lastmode]['idx'] = (iMin, iMed, iMax)
tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
num = 0 num = 0
pkgpc10 = syslpi = ''
if 'pkgpc10' in data and 'syslpi' in data:
pkgpc10 = data['pkgpc10']
syslpi = data['syslpi']
useturbo = True
res = data['result']
tVal = [float(data['suspend']), float(data['resume'])] tVal = [float(data['suspend']), float(data['resume'])]
list[mode]['data'].append([data['host'], data['kernel'], list[mode]['data'].append([data['host'], data['kernel'],
data['time'], tVal[0], tVal[1], data['url'], data['result'], data['time'], tVal[0], tVal[1], data['url'], res,
data['issues'], data['sus_worst'], data['sus_worsttime'], data['issues'], data['sus_worst'], data['sus_worsttime'],
data['res_worst'], data['res_worsttime']]) data['res_worst'], data['res_worsttime'], pkgpc10, syslpi])
idx = len(list[mode]['data']) - 1 idx = len(list[mode]['data']) - 1
if data['result'] not in cnt: if res.startswith('fail in'):
cnt[data['result']] = 1 res = 'fail'
if res not in cnt:
cnt[res] = 1
else: else:
cnt[data['result']] += 1 cnt[res] += 1
if data['result'] == 'pass': if res == 'pass':
for i in range(2): for i in range(2):
tMed[i].append(tVal[i]) tMed[i][tVal[i]] = idx
tAvg[i] += tVal[i] tAvg[i] += tVal[i]
if tMin[i] == 0 or tVal[i] < tMin[i]: if tMin[i] == 0 or tVal[i] < tMin[i]:
iMin[i] = idx iMin[i] = idx
...@@ -3597,7 +3804,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3597,7 +3804,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
for i in range(2): for i in range(2):
s = sorted(tMed[i]) s = sorted(tMed[i])
list[lastmode]['med'][i] = s[int(len(s)/2)] list[lastmode]['med'][i] = s[int(len(s)/2)]
iMed[i] = tMed[i].index(list[lastmode]['med'][i]) iMed[i] = tMed[i][list[lastmode]['med'][i]]
list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num]
list[lastmode]['min'] = tMin list[lastmode]['min'] = tMin
list[lastmode]['max'] = tMax list[lastmode]['max'] = tMax
...@@ -3613,19 +3820,21 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3613,19 +3820,21 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
td = '\t<td>{0}</td>\n' td = '\t<td>{0}</td>\n'
tdh = '\t<td{1}>{0}</td>\n' tdh = '\t<td{1}>{0}</td>\n'
tdlink = '\t<td><a href="{0}">html</a></td>\n' tdlink = '\t<td><a href="{0}">html</a></td>\n'
colspan = '14' if useturbo else '12'
# table header # table header
html += '<table class="summary">\n<tr>\n' + th.format('#') +\ html += '<table>\n<tr>\n' + th.format('#') +\
th.format('Mode') + th.format('Host') + th.format('Kernel') +\ th.format('Mode') + th.format('Host') + th.format('Kernel') +\
th.format('Test Time') + th.format('Result') + th.format('Issues') +\ th.format('Test Time') + th.format('Result') + th.format('Issues') +\
th.format('Suspend') + th.format('Resume') +\ th.format('Suspend') + th.format('Resume') +\
th.format('Worst Suspend Device') + th.format('SD Time') +\ th.format('Worst Suspend Device') + th.format('SD Time') +\
th.format('Worst Resume Device') + th.format('RD Time') +\ th.format('Worst Resume Device') + th.format('RD Time')
th.format('Detail') + '</tr>\n' if useturbo:
html += th.format('PkgPC10') + th.format('SysLPI')
html += th.format('Detail')+'</tr>\n'
# export list into html # export list into html
head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\ head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
'<td colspan=12 class="sus">Suspend Avg={2} '+\ '<td colspan='+colspan+' class="sus">Suspend Avg={2} '+\
'<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\ '<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\
'<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\ '<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\
'<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\ '<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\
...@@ -3634,7 +3843,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3634,7 +3843,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
'<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\ '<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\
'<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\ '<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\
'</tr>\n' '</tr>\n'
headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan=12></td></tr>\n' headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan='+\
colspan+'></td></tr>\n'
for mode in list: for mode in list:
# header line for each suspend mode # header line for each suspend mode
num = 0 num = 0
...@@ -3681,6 +3891,9 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3681,6 +3891,9 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
html += td.format('%.3f ms' % d[9]) if d[9] else td.format('') # sus_worst time html += td.format('%.3f ms' % d[9]) if d[9] else td.format('') # sus_worst time
html += td.format(d[10]) # res_worst html += td.format(d[10]) # res_worst
html += td.format('%.3f ms' % d[11]) if d[11] else td.format('') # res_worst time html += td.format('%.3f ms' % d[11]) if d[11] else td.format('') # res_worst time
if useturbo:
html += td.format(d[12]) # pkg_pc10
html += td.format(d[13]) # syslpi
html += tdlink.format(d[5]) if d[5] else td.format('') # url html += tdlink.format(d[5]) if d[5] else td.format('') # url
html += '</tr>\n' html += '</tr>\n'
num += 1 num += 1
...@@ -3690,6 +3903,115 @@ def createHTMLSummarySimple(testruns, htmlfile, title): ...@@ -3690,6 +3903,115 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
hf.write(html+'</table>\n</body>\n</html>\n') hf.write(html+'</table>\n</body>\n</html>\n')
hf.close() hf.close()
def createHTMLDeviceSummary(testruns, htmlfile, title):
html = summaryCSS('Device Summary - SleepGraph', False)
# create global device list from all tests
devall = dict()
for data in testruns:
host, url, devlist = data['host'], data['url'], data['devlist']
for type in devlist:
if type not in devall:
devall[type] = dict()
mdevlist, devlist = devall[type], data['devlist'][type]
for name in devlist:
length = devlist[name]
if name not in mdevlist:
mdevlist[name] = {'name': name, 'host': host,
'worst': length, 'total': length, 'count': 1,
'url': url}
else:
if length > mdevlist[name]['worst']:
mdevlist[name]['worst'] = length
mdevlist[name]['url'] = url
mdevlist[name]['host'] = host
mdevlist[name]['total'] += length
mdevlist[name]['count'] += 1
# generate the html
th = '\t<th>{0}</th>\n'
td = '\t<td align=center>{0}</td>\n'
tdr = '\t<td align=right>{0}</td>\n'
tdlink = '\t<td align=center><a href="{0}">html</a></td>\n'
limit = 1
for type in sorted(devall, reverse=True):
num = 0
devlist = devall[type]
# table header
html += '<div class="stamp">%s (%s devices > %d ms)</div><table>\n' % \
(title, type.upper(), limit)
html += '<tr>\n' + '<th align=right>Device Name</th>' +\
th.format('Average Time') + th.format('Count') +\
th.format('Worst Time') + th.format('Host (worst time)') +\
th.format('Link (worst time)') + '</tr>\n'
for name in sorted(devlist, key=lambda k:devlist[k]['worst'], reverse=True):
data = devall[type][name]
data['average'] = data['total'] / data['count']
if data['average'] < limit:
continue
# row classes - alternate row color
rcls = ['alt'] if num % 2 == 1 else []
html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
html += tdr.format(data['name']) # name
html += td.format('%.3f ms' % data['average']) # average
html += td.format(data['count']) # count
html += td.format('%.3f ms' % data['worst']) # worst
html += td.format(data['host']) # host
html += tdlink.format(data['url']) # url
html += '</tr>\n'
num += 1
html += '</table>\n'
# flush the data to file
hf = open(htmlfile, 'w')
hf.write(html+'</body>\n</html>\n')
hf.close()
return devall
def createHTMLIssuesSummary(testruns, issues, htmlfile, title, extra=''):
multihost = len([e for e in issues if len(e['urls']) > 1]) > 0
html = summaryCSS('Issues Summary - SleepGraph', False)
total = len(testruns)
# generate the html
th = '\t<th>{0}</th>\n'
td = '\t<td align={0}>{1}</td>\n'
tdlink = '<a href="{1}">{0}</a>'
subtitle = '%d issues' % len(issues) if len(issues) > 0 else 'no issues'
html += '<div class="stamp">%s (%s)</div><table>\n' % (title, subtitle)
html += '<tr>\n' + th.format('Issue') + th.format('Count')
if multihost:
html += th.format('Hosts')
html += th.format('Tests') + th.format('Fail Rate') +\
th.format('First Instance') + '</tr>\n'
num = 0
for e in sorted(issues, key=lambda v:v['count'], reverse=True):
testtotal = 0
links = []
for host in sorted(e['urls']):
links.append(tdlink.format(host, e['urls'][host][0]))
testtotal += len(e['urls'][host])
rate = '%d/%d (%.2f%%)' % (testtotal, total, 100*float(testtotal)/float(total))
# row classes - alternate row color
rcls = ['alt'] if num % 2 == 1 else []
html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n'
html += td.format('left', e['line']) # issue
html += td.format('center', e['count']) # count
if multihost:
html += td.format('center', len(e['urls'])) # hosts
html += td.format('center', testtotal) # test count
html += td.format('center', rate) # test rate
html += td.format('center nowrap', '<br>'.join(links)) # links
html += '</tr>\n'
num += 1
# flush the data to file
hf = open(htmlfile, 'w')
hf.write(html+'</table>\n'+extra+'</body>\n</html>\n')
hf.close()
return issues
def ordinal(value): def ordinal(value):
suffix = 'th' suffix = 'th'
if value < 10 or value > 19: if value < 10 or value > 19:
...@@ -3991,7 +4313,7 @@ def createHTML(testruns, testfail): ...@@ -3991,7 +4313,7 @@ def createHTML(testruns, testfail):
for word in phase.split('_'): for word in phase.split('_'):
id += word[0] id += word[0]
order = '%.2f' % ((p['order'] * pdelta) + pmargin) order = '%.2f' % ((p['order'] * pdelta) + pmargin)
name = string.replace(phase, '_', ' &nbsp;') name = phase.replace('_', ' &nbsp;')
devtl.html += devtl.html_legend.format(order, p['color'], name, id) devtl.html += devtl.html_legend.format(order, p['color'], name, id)
devtl.html += '</div>\n' devtl.html += '</div>\n'
...@@ -4580,6 +4902,7 @@ def setRuntimeSuspend(before=True): ...@@ -4580,6 +4902,7 @@ def setRuntimeSuspend(before=True):
def executeSuspend(): def executeSuspend():
pm = ProcessMonitor() pm = ProcessMonitor()
tp = sysvals.tpath tp = sysvals.tpath
wifi = sysvals.checkWifi()
testdata = [] testdata = []
battery = True if getBattery() else False battery = True if getBattery() else False
# run these commands to prepare the system for suspend # run these commands to prepare the system for suspend
...@@ -4613,6 +4936,7 @@ def executeSuspend(): ...@@ -4613,6 +4936,7 @@ def executeSuspend():
pprint('SUSPEND START') pprint('SUSPEND START')
else: else:
pprint('SUSPEND START (press a key to resume)') pprint('SUSPEND START (press a key to resume)')
sysvals.mcelog(True)
bat1 = getBattery() if battery else False bat1 = getBattery() if battery else False
# set rtcwake # set rtcwake
if(sysvals.rtcwake): if(sysvals.rtcwake):
...@@ -4644,6 +4968,16 @@ def executeSuspend(): ...@@ -4644,6 +4968,16 @@ def executeSuspend():
pf = open(sysvals.diskpowerfile, 'w') pf = open(sysvals.diskpowerfile, 'w')
pf.write(sysvals.diskmode) pf.write(sysvals.diskmode)
pf.close() pf.close()
if mode == 'freeze' and sysvals.haveTurbostat():
# execution will pause here
turbo = sysvals.turbostat()
if '|' in turbo:
tdata['turbo'] = turbo
else:
tdata['error'] = turbo
else:
if sysvals.haveTurbostat():
sysvals.vprint('WARNING: ignoring turbostat in mode "%s"' % mode)
pf = open(sysvals.powerfile, 'w') pf = open(sysvals.powerfile, 'w')
pf.write(mode) pf.write(mode)
# execution will pause here # execution will pause here
...@@ -4664,9 +4998,14 @@ def executeSuspend(): ...@@ -4664,9 +4998,14 @@ def executeSuspend():
sysvals.fsetVal('RESUME COMPLETE', 'trace_marker') sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'): if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
tdata['fw'] = getFPDT(False) tdata['fw'] = getFPDT(False)
mcelog = sysvals.mcelog()
if mcelog:
tdata['mcelog'] = mcelog
bat2 = getBattery() if battery else False bat2 = getBattery() if battery else False
if battery and bat1 and bat2: if battery and bat1 and bat2:
tdata['bat'] = (bat1, bat2) tdata['bat'] = (bat1, bat2)
if 'device' in wifi and 'ip' in wifi:
tdata['wifi'] = (wifi, sysvals.checkWifi())
testdata.append(tdata) testdata.append(tdata)
# stop ftrace # stop ftrace
if(sysvals.usecallgraph or sysvals.usetraceevents): if(sysvals.usecallgraph or sysvals.usetraceevents):
...@@ -4686,6 +5025,7 @@ def executeSuspend(): ...@@ -4686,6 +5025,7 @@ def executeSuspend():
op.close() op.close()
sysvals.fsetVal('', 'trace') sysvals.fsetVal('', 'trace')
devProps() devProps()
return testdata
def readFile(file): def readFile(file):
if os.path.islink(file): if os.path.islink(file):
...@@ -4772,7 +5112,7 @@ def deviceInfo(output=''): ...@@ -4772,7 +5112,7 @@ def deviceInfo(output=''):
ms2nice(power['runtime_active_time']), \ ms2nice(power['runtime_active_time']), \
ms2nice(power['runtime_suspended_time'])) ms2nice(power['runtime_suspended_time']))
for i in sorted(lines): for i in sorted(lines):
print lines[i] print(lines[i])
return res return res
# Function: devProps # Function: devProps
...@@ -4905,12 +5245,12 @@ def getModes(): ...@@ -4905,12 +5245,12 @@ def getModes():
modes = [] modes = []
if(os.path.exists(sysvals.powerfile)): if(os.path.exists(sysvals.powerfile)):
fp = open(sysvals.powerfile, 'r') fp = open(sysvals.powerfile, 'r')
modes = string.split(fp.read()) modes = fp.read().split()
fp.close() fp.close()
if(os.path.exists(sysvals.mempowerfile)): if(os.path.exists(sysvals.mempowerfile)):
deep = False deep = False
fp = open(sysvals.mempowerfile, 'r') fp = open(sysvals.mempowerfile, 'r')
for m in string.split(fp.read()): for m in fp.read().split():
memmode = m.strip('[]') memmode = m.strip('[]')
if memmode == 'deep': if memmode == 'deep':
deep = True deep = True
...@@ -4921,7 +5261,7 @@ def getModes(): ...@@ -4921,7 +5261,7 @@ def getModes():
modes.remove('mem') modes.remove('mem')
if('disk' in modes and os.path.exists(sysvals.diskpowerfile)): if('disk' in modes and os.path.exists(sysvals.diskpowerfile)):
fp = open(sysvals.diskpowerfile, 'r') fp = open(sysvals.diskpowerfile, 'r')
for m in string.split(fp.read()): for m in fp.read().split():
modes.append('disk-%s' % m.strip('[]')) modes.append('disk-%s' % m.strip('[]'))
fp.close() fp.close()
return modes return modes
...@@ -4984,14 +5324,15 @@ def dmidecode(mempath, fatal=False): ...@@ -4984,14 +5324,15 @@ def dmidecode(mempath, fatal=False):
continue continue
# read in the memory for scanning # read in the memory for scanning
fp = open(mempath, 'rb')
try: try:
fp = open(mempath, 'rb')
fp.seek(memaddr) fp.seek(memaddr)
buf = fp.read(memsize) buf = fp.read(memsize)
except: except:
if(fatal): if(fatal):
doError('DMI table is unreachable, sorry') doError('DMI table is unreachable, sorry')
else: else:
pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
return out return out
fp.close() fp.close()
...@@ -5014,14 +5355,15 @@ def dmidecode(mempath, fatal=False): ...@@ -5014,14 +5355,15 @@ def dmidecode(mempath, fatal=False):
return out return out
# read in the SM or DMI table # read in the SM or DMI table
fp = open(mempath, 'rb')
try: try:
fp = open(mempath, 'rb')
fp.seek(base) fp.seek(base)
buf = fp.read(length) buf = fp.read(length)
except: except:
if(fatal): if(fatal):
doError('DMI table is unreachable, sorry') doError('DMI table is unreachable, sorry')
else: else:
pprint('WARNING: /dev/mem is not readable, ignoring DMI data')
return out return out
fp.close() fp.close()
...@@ -5165,7 +5507,11 @@ def getFPDT(output): ...@@ -5165,7 +5507,11 @@ def getFPDT(output):
i = 0 i = 0
fwData = [0, 0] fwData = [0, 0]
records = buf[36:] records = buf[36:]
try:
fp = open(sysvals.mempath, 'rb') fp = open(sysvals.mempath, 'rb')
except:
pprint('WARNING: /dev/mem is not readable, ignoring the FPDT data')
return False
while(i < len(records)): while(i < len(records)):
header = struct.unpack('HBB', records[i:i+4]) header = struct.unpack('HBB', records[i:i+4])
if(header[0] not in rectype): if(header[0] not in rectype):
...@@ -5282,6 +5628,7 @@ def statusCheck(probecheck=False): ...@@ -5282,6 +5628,7 @@ def statusCheck(probecheck=False):
pprint(' is ftrace supported: %s' % res) pprint(' is ftrace supported: %s' % res)
# check if kprobes are available # check if kprobes are available
if sysvals.usekprobes:
res = sysvals.colorText('NO') res = sysvals.colorText('NO')
sysvals.usekprobes = sysvals.verifyKprobes() sysvals.usekprobes = sysvals.verifyKprobes()
if(sysvals.usekprobes): if(sysvals.usekprobes):
...@@ -5376,6 +5723,8 @@ def getArgFloat(name, args, min, max, main=True): ...@@ -5376,6 +5723,8 @@ def getArgFloat(name, args, min, max, main=True):
def processData(live=False): def processData(live=False):
pprint('PROCESSING DATA') pprint('PROCESSING DATA')
sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
(sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
error = '' error = ''
if(sysvals.usetraceevents): if(sysvals.usetraceevents):
testruns, error = parseTraceLog(live) testruns, error = parseTraceLog(live)
...@@ -5388,13 +5737,36 @@ def processData(live=False): ...@@ -5388,13 +5737,36 @@ def processData(live=False):
parseKernelLog(data) parseKernelLog(data)
if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)): if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
appendIncompleteTraceLog(testruns) appendIncompleteTraceLog(testruns)
sysvals.vprint('System Info:')
for key in sorted(sysvals.stamp):
sysvals.vprint(' %-8s : %s' % (key.upper(), sysvals.stamp[key]))
if sysvals.kparams:
sysvals.vprint('Kparams:\n %s' % sysvals.kparams)
sysvals.vprint('Command:\n %s' % sysvals.cmdline) sysvals.vprint('Command:\n %s' % sysvals.cmdline)
for data in testruns: for data in testruns:
if data.mcelog:
sysvals.vprint('MCELOG Data:')
for line in data.mcelog.split('\n'):
sysvals.vprint(' %s' % line)
if data.turbostat:
idx, s = 0, 'Turbostat:\n '
for val in data.turbostat.split('|'):
idx += len(val) + 1
if idx >= 80:
idx = 0
s += '\n '
s += val + ' '
sysvals.vprint(s)
if data.battery: if data.battery:
a1, c1, a2, c2 = data.battery a1, c1, a2, c2 = data.battery
s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \ s = 'Battery:\n Before - AC: %s, Charge: %d\n After - AC: %s, Charge: %d' % \
(a1, int(c1), a2, int(c2)) (a1, int(c1), a2, int(c2))
sysvals.vprint(s) sysvals.vprint(s)
if data.wifi:
w = data.wifi.replace('|', ' ').split(',')
s = 'Wifi:\n Before %s\n After %s' % \
(w[0], w[1])
sysvals.vprint(s)
data.printDetails() data.printDetails()
if sysvals.cgdump: if sysvals.cgdump:
for data in testruns: for data in testruns:
...@@ -5418,11 +5790,14 @@ def processData(live=False): ...@@ -5418,11 +5790,14 @@ def processData(live=False):
# Function: rerunTest # Function: rerunTest
# Description: # Description:
# generate an output from an existing set of ftrace/dmesg logs # generate an output from an existing set of ftrace/dmesg logs
def rerunTest(): def rerunTest(htmlfile=''):
if sysvals.ftracefile: if sysvals.ftracefile:
doesTraceLogHaveTraceEvents() doesTraceLogHaveTraceEvents()
if not sysvals.dmesgfile and not sysvals.usetraceevents: if not sysvals.dmesgfile and not sysvals.usetraceevents:
doError('recreating this html output requires a dmesg file') doError('recreating this html output requires a dmesg file')
if htmlfile:
sysvals.htmlfile = htmlfile
else:
sysvals.setOutputFile() sysvals.setOutputFile()
if os.path.exists(sysvals.htmlfile): if os.path.exists(sysvals.htmlfile):
if not os.path.isfile(sysvals.htmlfile): if not os.path.isfile(sysvals.htmlfile):
...@@ -5442,14 +5817,18 @@ def runTest(n=0): ...@@ -5442,14 +5817,18 @@ def runTest(n=0):
sysvals.initTestOutput('suspend') sysvals.initTestOutput('suspend')
# execute the test # execute the test
executeSuspend() testdata = executeSuspend()
sysvals.cleanupFtrace() sysvals.cleanupFtrace()
if sysvals.skiphtml: if sysvals.skiphtml:
sysvals.sudoUserchown(sysvals.testdir) sysvals.sudoUserchown(sysvals.testdir)
return return
if not testdata[0]['error']:
testruns, stamp = processData(True) testruns, stamp = processData(True)
for data in testruns: for data in testruns:
del data del data
else:
stamp = testdata[0]
sysvals.sudoUserchown(sysvals.testdir) sysvals.sudoUserchown(sysvals.testdir)
sysvals.outputResult(stamp, n) sysvals.outputResult(stamp, n)
if 'error' in stamp: if 'error' in stamp:
...@@ -5479,10 +5858,13 @@ def find_in_html(html, start, end, firstonly=True): ...@@ -5479,10 +5858,13 @@ def find_in_html(html, start, end, firstonly=True):
return '' return ''
return out return out
def data_from_html(file, outpath, devlist=False): def data_from_html(file, outpath, issues, fulldetail=False):
html = open(file, 'r').read() html = open(file, 'r').read()
sysvals.htmlfile = os.path.relpath(file, outpath)
# extract general info
suspend = find_in_html(html, 'Kernel Suspend', 'ms') suspend = find_in_html(html, 'Kernel Suspend', 'ms')
resume = find_in_html(html, 'Kernel Resume', 'ms') resume = find_in_html(html, 'Kernel Resume', 'ms')
sysinfo = find_in_html(html, '<div class="stamp sysinfo">', '</div>')
line = find_in_html(html, '<div class="stamp">', '</div>') line = find_in_html(html, '<div class="stamp">', '</div>')
stmp = line.split() stmp = line.split()
if not suspend or not resume or len(stmp) != 8: if not suspend or not resume or len(stmp) != 8:
...@@ -5491,6 +5873,7 @@ def data_from_html(file, outpath, devlist=False): ...@@ -5491,6 +5873,7 @@ def data_from_html(file, outpath, devlist=False):
dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p') dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p')
except: except:
return False return False
sysvals.hostname = stmp[0]
tstr = dt.strftime('%Y/%m/%d %H:%M:%S') tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>') error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
if error: if error:
...@@ -5501,13 +5884,45 @@ def data_from_html(file, outpath, devlist=False): ...@@ -5501,13 +5884,45 @@ def data_from_html(file, outpath, devlist=False):
result = 'fail' result = 'fail'
else: else:
result = 'pass' result = 'pass'
# extract error info
ilist = [] ilist = []
e = find_in_html(html, 'class="err"[\w=":;\.%\- ]*>', '&rarr;</div>', False) extra = dict()
for i in list(set(e)): log = find_in_html(html, '<div id="dmesglog" style="display:none;">',
ilist.append('%sx%d' % (i, e.count(i)) if e.count(i) > 1 else i) '</div>').strip()
if log:
d = Data(0)
d.end = 999999999
d.dmesgtext = log.split('\n')
msglist = d.extractErrorInfo()
for msg in msglist:
sysvals.errorSummary(issues, msg)
if stmp[2] == 'freeze':
extra = d.turbostatInfo()
elist = dict()
for dir in d.errorinfo:
for err in d.errorinfo[dir]:
if err[0] not in elist:
elist[err[0]] = 0
elist[err[0]] += 1
for i in elist:
ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
low = find_in_html(html, 'freeze time: <b>', ' ms</b>') low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
if low and '|' in low: if low and '|' in low:
ilist.append('FREEZEx%d' % len(low.split('|'))) issue = 'FREEZEx%d' % len(low.split('|'))
match = [i for i in issues if i['match'] == issue]
if len(match) > 0:
match[0]['count'] += 1
if sysvals.hostname not in match[0]['urls']:
match[0]['urls'][sysvals.hostname] = [sysvals.htmlfile]
elif sysvals.htmlfile not in match[0]['urls'][sysvals.hostname]:
match[0]['urls'][sysvals.hostname].append(sysvals.htmlfile)
else:
issues.append({
'match': issue, 'count': 1, 'line': issue,
'urls': {sysvals.hostname: [sysvals.htmlfile]},
})
ilist.append(issue)
# extract device info
devices = dict() devices = dict()
for line in html.split('\n'): for line in html.split('\n'):
m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line) m = re.match(' *<div id=\"[a,0-9]*\" *title=\"(?P<title>.*)\" class=\"thread.*', line)
...@@ -5519,48 +5934,49 @@ def data_from_html(file, outpath, devlist=False): ...@@ -5519,48 +5934,49 @@ def data_from_html(file, outpath, devlist=False):
name, time, phase = m.group('n'), m.group('t'), m.group('p') name, time, phase = m.group('n'), m.group('t'), m.group('p')
if ' async' in name or ' sync' in name: if ' async' in name or ' sync' in name:
name = ' '.join(name.split(' ')[:-1]) name = ' '.join(name.split(' ')[:-1])
d = phase.split('_')[0] if phase.startswith('suspend'):
d = 'suspend'
elif phase.startswith('resume'):
d = 'resume'
else:
continue
if d not in devices: if d not in devices:
devices[d] = dict() devices[d] = dict()
if name not in devices[d]: if name not in devices[d]:
devices[d][name] = 0.0 devices[d][name] = 0.0
devices[d][name] += float(time) devices[d][name] += float(time)
worst = {'suspend': {'name':'', 'time': 0.0}, # create worst device info
'resume': {'name':'', 'time': 0.0}} worst = dict()
for d in devices: for d in ['suspend', 'resume']:
if d not in worst: worst[d] = {'name':'', 'time': 0.0}
worst[d] = dict() dev = devices[d] if d in devices else 0
dev = devices[d] if dev and len(dev.keys()) > 0:
if len(dev.keys()) > 0:
n = sorted(dev, key=dev.get, reverse=True)[0] n = sorted(dev, key=dev.get, reverse=True)[0]
worst[d]['name'], worst[d]['time'] = n, dev[n] worst[d]['name'], worst[d]['time'] = n, dev[n]
data = { data = {
'mode': stmp[2], 'mode': stmp[2],
'host': stmp[0], 'host': stmp[0],
'kernel': stmp[1], 'kernel': stmp[1],
'sysinfo': sysinfo,
'time': tstr, 'time': tstr,
'result': result, 'result': result,
'issues': ' '.join(ilist), 'issues': ' '.join(ilist),
'suspend': suspend, 'suspend': suspend,
'resume': resume, 'resume': resume,
'devlist': devices,
'sus_worst': worst['suspend']['name'], 'sus_worst': worst['suspend']['name'],
'sus_worsttime': worst['suspend']['time'], 'sus_worsttime': worst['suspend']['time'],
'res_worst': worst['resume']['name'], 'res_worst': worst['resume']['name'],
'res_worsttime': worst['resume']['time'], 'res_worsttime': worst['resume']['time'],
'url': os.path.relpath(file, outpath), 'url': sysvals.htmlfile,
} }
if devlist: for key in extra:
data['devlist'] = devices data[key] = extra[key]
if fulldetail:
data['funclist'] = find_in_html(html, '<div title="', '" class="traceevent"', False)
return data return data
# Function: runSummary def genHtml(subdir):
# Description:
# create a summary of tests in a sub-directory
def runSummary(subdir, local=True, genhtml=False):
inpath = os.path.abspath(subdir)
outpath = os.path.abspath('.') if local else inpath
pprint('Generating a summary of folder "%s"' % inpath)
if genhtml:
for dirname, dirnames, filenames in os.walk(subdir): for dirname, dirnames, filenames in os.walk(subdir):
sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = '' sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
for filename in filenames: for filename in filenames:
...@@ -5575,26 +5991,41 @@ def runSummary(subdir, local=True, genhtml=False): ...@@ -5575,26 +5991,41 @@ def runSummary(subdir, local=True, genhtml=False):
if sysvals.dmesgfile: if sysvals.dmesgfile:
pprint('DMESG : %s' % sysvals.dmesgfile) pprint('DMESG : %s' % sysvals.dmesgfile)
rerunTest() rerunTest()
# Function: runSummary
# Description:
# create a summary of tests in a sub-directory
def runSummary(subdir, local=True, genhtml=False):
inpath = os.path.abspath(subdir)
outpath = os.path.abspath('.') if local else inpath
pprint('Generating a summary of folder:\n %s' % inpath)
if genhtml:
genHtml(subdir)
issues = []
testruns = [] testruns = []
desc = {'host':[],'mode':[],'kernel':[]} desc = {'host':[],'mode':[],'kernel':[]}
for dirname, dirnames, filenames in os.walk(subdir): for dirname, dirnames, filenames in os.walk(subdir):
for filename in filenames: for filename in filenames:
if(not re.match('.*.html', filename)): if(not re.match('.*.html', filename)):
continue continue
data = data_from_html(os.path.join(dirname, filename), outpath) data = data_from_html(os.path.join(dirname, filename), outpath, issues)
if(not data): if(not data):
continue continue
testruns.append(data) testruns.append(data)
for key in desc: for key in desc:
if data[key] not in desc[key]: if data[key] not in desc[key]:
desc[key].append(data[key]) desc[key].append(data[key])
outfile = os.path.join(outpath, 'summary.html') pprint('Summary files:')
pprint('Summary file: %s' % outfile)
if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1: if len(desc['host']) == len(desc['mode']) == len(desc['kernel']) == 1:
title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0]) title = '%s %s %s' % (desc['host'][0], desc['kernel'][0], desc['mode'][0])
else: else:
title = inpath title = inpath
createHTMLSummarySimple(testruns, outfile, title) createHTMLSummarySimple(testruns, os.path.join(outpath, 'summary.html'), title)
pprint(' summary.html - tabular list of test data found')
createHTMLDeviceSummary(testruns, os.path.join(outpath, 'summary-devices.html'), title)
pprint(' summary-devices.html - kernel device list sorted by total execution time')
createHTMLIssuesSummary(testruns, issues, os.path.join(outpath, 'summary-issues.html'), title)
pprint(' summary-issues.html - kernel issues found sorted by frequency')
# Function: checkArgBool # Function: checkArgBool
# Description: # Description:
...@@ -5839,6 +6270,7 @@ def printHelp(): ...@@ -5839,6 +6270,7 @@ def printHelp():
' default: suspend-{date}-{time}\n'\ ' default: suspend-{date}-{time}\n'\
' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\ ' -rtcwake t Wakeup t seconds after suspend, set t to "off" to disable (default: 15)\n'\
' -addlogs Add the dmesg and ftrace logs to the html output\n'\ ' -addlogs Add the dmesg and ftrace logs to the html output\n'\
' -turbostat Use turbostat to execute the command in freeze mode (default: disabled)\n'\
' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\ ' -srgap Add a visible gap in the timeline between sus/res (default: disabled)\n'\
' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\ ' -skiphtml Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
' -result fn Export a results table to a text file for parsing.\n'\ ' -result fn Export a results table to a text file for parsing.\n'\
...@@ -5860,6 +6292,7 @@ def printHelp(): ...@@ -5860,6 +6292,7 @@ def printHelp():
' be created in a new subdirectory with a summary page.\n'\ ' be created in a new subdirectory with a summary page.\n'\
' [debug]\n'\ ' [debug]\n'\
' -f Use ftrace to create device callgraphs (default: disabled)\n'\ ' -f Use ftrace to create device callgraphs (default: disabled)\n'\
' -ftop Use ftrace on the top level call: "%s" (default: disabled)\n'\
' -maxdepth N limit the callgraph data to N call levels (default: 0=all)\n'\ ' -maxdepth N limit the callgraph data to N call levels (default: 0=all)\n'\
' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\ ' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\
' -fadd file Add functions to be graphed in the timeline from a list in a text file\n'\ ' -fadd file Add functions to be graphed in the timeline from a list in a text file\n'\
...@@ -5879,6 +6312,7 @@ def printHelp(): ...@@ -5879,6 +6312,7 @@ def printHelp():
' -status Test to see if the system is enabled to run this tool\n'\ ' -status Test to see if the system is enabled to run this tool\n'\
' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\ ' -fpdt Print out the contents of the ACPI Firmware Performance Data Table\n'\
' -battery Print out battery info (if available)\n'\ ' -battery Print out battery info (if available)\n'\
' -wifi Print out wifi connection info (if wireless-tools and device exists)\n'\
' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\ ' -x<mode> Test xset by toggling the given mode (on/off/standby/suspend)\n'\
' -sysinfo Print out system info extracted from BIOS\n'\ ' -sysinfo Print out system info extracted from BIOS\n'\
' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\ ' -devinfo Print out the pm settings of all devices which support runtime suspend\n'\
...@@ -5888,7 +6322,7 @@ def printHelp(): ...@@ -5888,7 +6322,7 @@ def printHelp():
' [redo]\n'\ ' [redo]\n'\
' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)\n'\ ' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)\n'\
' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)\n'\ ' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)\n'\
'' % (sysvals.title, sysvals.version, sysvals.suspendmode)) '' % (sysvals.title, sysvals.version, sysvals.suspendmode, sysvals.ftopfunc))
return True return True
# ----------------- MAIN -------------------- # ----------------- MAIN --------------------
...@@ -5898,7 +6332,7 @@ if __name__ == '__main__': ...@@ -5898,7 +6332,7 @@ if __name__ == '__main__':
cmd = '' cmd = ''
simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
'-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby', '-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby',
'-xsuspend', '-xinit', '-xreset', '-xstat'] '-xsuspend', '-xinit', '-xreset', '-xstat', '-wifi']
if '-f' in sys.argv: if '-f' in sys.argv:
sysvals.cgskip = sysvals.configFile('cgskip.txt') sysvals.cgskip = sysvals.configFile('cgskip.txt')
# loop through the command line arguments # loop through the command line arguments
...@@ -5930,6 +6364,10 @@ if __name__ == '__main__': ...@@ -5930,6 +6364,10 @@ if __name__ == '__main__':
sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000) sysvals.postdelay = getArgInt('-postdelay', args, 0, 60000)
elif(arg == '-f'): elif(arg == '-f'):
sysvals.usecallgraph = True sysvals.usecallgraph = True
elif(arg == '-ftop'):
sysvals.usecallgraph = True
sysvals.ftop = True
sysvals.usekprobes = False
elif(arg == '-skiphtml'): elif(arg == '-skiphtml'):
sysvals.skiphtml = True sysvals.skiphtml = True
elif(arg == '-cgdump'): elif(arg == '-cgdump'):
...@@ -5940,10 +6378,16 @@ if __name__ == '__main__': ...@@ -5940,10 +6378,16 @@ if __name__ == '__main__':
genhtml = True genhtml = True
elif(arg == '-addlogs'): elif(arg == '-addlogs'):
sysvals.dmesglog = sysvals.ftracelog = True sysvals.dmesglog = sysvals.ftracelog = True
elif(arg == '-nologs'):
sysvals.dmesglog = sysvals.ftracelog = False
elif(arg == '-addlogdmesg'): elif(arg == '-addlogdmesg'):
sysvals.dmesglog = True sysvals.dmesglog = True
elif(arg == '-addlogftrace'): elif(arg == '-addlogftrace'):
sysvals.ftracelog = True sysvals.ftracelog = True
elif(arg == '-turbostat'):
sysvals.tstat = True
if not sysvals.haveTurbostat():
doError('Turbostat command not found')
elif(arg == '-verbose'): elif(arg == '-verbose'):
sysvals.verbose = True sysvals.verbose = True
elif(arg == '-proc'): elif(arg == '-proc'):
...@@ -6013,6 +6457,12 @@ if __name__ == '__main__': ...@@ -6013,6 +6457,12 @@ if __name__ == '__main__':
except: except:
doError('No callgraph functions supplied', True) doError('No callgraph functions supplied', True)
sysvals.setCallgraphFilter(val) sysvals.setCallgraphFilter(val)
elif(arg == '-skipkprobe'):
try:
val = args.next()
except:
doError('No kprobe functions supplied', True)
sysvals.skipKprobes(val)
elif(arg == '-cgskip'): elif(arg == '-cgskip'):
try: try:
val = args.next() val = args.next()
...@@ -6151,7 +6601,7 @@ if __name__ == '__main__': ...@@ -6151,7 +6601,7 @@ if __name__ == '__main__':
elif(cmd == 'devinfo'): elif(cmd == 'devinfo'):
deviceInfo() deviceInfo()
elif(cmd == 'modes'): elif(cmd == 'modes'):
print getModes() pprint(getModes())
elif(cmd == 'flist'): elif(cmd == 'flist'):
sysvals.getFtraceFilterFunctions(True) sysvals.getFtraceFilterFunctions(True)
elif(cmd == 'flistall'): elif(cmd == 'flistall'):
...@@ -6163,11 +6613,18 @@ if __name__ == '__main__': ...@@ -6163,11 +6613,18 @@ if __name__ == '__main__':
ret = displayControl(cmd[1:]) ret = displayControl(cmd[1:])
elif(cmd == 'xstat'): elif(cmd == 'xstat'):
pprint('Display Status: %s' % displayControl('stat').upper()) pprint('Display Status: %s' % displayControl('stat').upper())
elif(cmd == 'wifi'):
out = sysvals.checkWifi()
if 'device' not in out:
pprint('WIFI interface not found')
else:
for key in sorted(out):
pprint('%6s: %s' % (key.upper(), out[key]))
sys.exit(ret) sys.exit(ret)
# if instructed, re-analyze existing data files # if instructed, re-analyze existing data files
if(sysvals.notestrun): if(sysvals.notestrun):
stamp = rerunTest() stamp = rerunTest(sysvals.outdir)
sysvals.outputResult(stamp) sysvals.outputResult(stamp)
sys.exit(0) sys.exit(0)
...@@ -6204,7 +6661,7 @@ if __name__ == '__main__': ...@@ -6204,7 +6661,7 @@ if __name__ == '__main__':
s = 'suspend-x%d' % sysvals.multitest['count'] s = 'suspend-x%d' % sysvals.multitest['count']
sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S') sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S')
if not os.path.isdir(sysvals.outdir): if not os.path.isdir(sysvals.outdir):
os.mkdir(sysvals.outdir) os.makedirs(sysvals.outdir)
for i in range(sysvals.multitest['count']): for i in range(sysvals.multitest['count']):
if(i != 0): if(i != 0):
pprint('Waiting %d seconds...' % (sysvals.multitest['delay'])) pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment